mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-07 05:42:50 +00:00
Initial commit
This commit is contained in:
160
plugins/modules/network/onyx/onyx_aaa.py
Normal file
160
plugins/modules/network/onyx/onyx_aaa.py
Normal file
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_aaa
|
||||
author: "Sara Touqan (@sarato)"
|
||||
short_description: Configures AAA parameters
|
||||
description:
|
||||
- This module provides declarative management of AAA protocol params
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
tacacs_accounting_enabled:
|
||||
description:
|
||||
- Configures accounting settings.
|
||||
type: bool
|
||||
auth_default_user:
|
||||
description:
|
||||
- Sets local user default mapping.
|
||||
type: str
|
||||
choices: ['admin', 'monitor']
|
||||
auth_order:
|
||||
description:
|
||||
- Sets the order on how to handle remote to local user mappings.
|
||||
type: str
|
||||
choices: ['local-only', 'remote-first', 'remote-only']
|
||||
auth_fallback_enabled:
|
||||
description:
|
||||
- Enables/Disables fallback server-err option.
|
||||
type: bool
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configures aaa
|
||||
onyx_aaa:
|
||||
tacacs_accounting_enabled: yes
|
||||
auth_default_user: monitor
|
||||
auth_order: local-only
|
||||
auth_fallback_enabled: false
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- aaa accounting changes default stop-only tacacs+
|
||||
- no aaa accounting changes default stop-only tacacs+
|
||||
- aaa authorization map default-user <user>
|
||||
- aaa authorization map order <order>
|
||||
- aaa authorization map fallback server-err
|
||||
- no aaa authorization map fallback server-err
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxAAAModule(BaseOnyxModule):
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
element_spec = dict(
|
||||
tacacs_accounting_enabled=dict(type='bool'),
|
||||
auth_default_user=dict(type='str', choices=['admin', 'monitor']),
|
||||
auth_order=dict(type='str', choices=['local-only', 'remote-first', 'remote-only']),
|
||||
auth_fallback_enabled=dict(type='bool')
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _set_aaa_config(self, all_aaa_config):
|
||||
aaa_config = all_aaa_config[0]
|
||||
self._current_config['auth_default_user'] = aaa_config.get("Default User")
|
||||
self._current_config['auth_order'] = aaa_config.get("Map Order")
|
||||
auth_fallback_enabled = aaa_config.get("Fallback on server-err")
|
||||
if auth_fallback_enabled == "yes":
|
||||
self._current_config['auth_fallback_enabled'] = True
|
||||
else:
|
||||
self._current_config['auth_fallback_enabled'] = False
|
||||
aaa_config_2 = all_aaa_config[2]
|
||||
accounting_message = aaa_config_2.get("message")
|
||||
if accounting_message == "No accounting methods configured.":
|
||||
self._current_config['tacacs_accounting_enabled'] = False
|
||||
else:
|
||||
self._current_config['tacacs_accounting_enabled'] = True
|
||||
|
||||
def _show_aaa_config(self):
|
||||
cmd = "show aaa"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
aaa_config = self._show_aaa_config()
|
||||
if aaa_config:
|
||||
self._set_aaa_config(aaa_config)
|
||||
|
||||
def generate_commands(self):
|
||||
tacacs_accounting_enabled = self._required_config.get("tacacs_accounting_enabled")
|
||||
if tacacs_accounting_enabled is not None:
|
||||
current_accounting_enabled = self._current_config.get("tacacs_accounting_enabled")
|
||||
if current_accounting_enabled != tacacs_accounting_enabled:
|
||||
if tacacs_accounting_enabled is True:
|
||||
self._commands.append('aaa accounting changes default stop-only tacacs+')
|
||||
else:
|
||||
self._commands.append('no aaa accounting changes default stop-only tacacs+')
|
||||
|
||||
auth_default_user = self._required_config.get("auth_default_user")
|
||||
if auth_default_user is not None:
|
||||
current_user = self._current_config.get("auth_default_user")
|
||||
if current_user != auth_default_user:
|
||||
self._commands.append('aaa authorization map default-user {0}' .format(auth_default_user))
|
||||
|
||||
auth_order = self._required_config.get("auth_order")
|
||||
if auth_order is not None:
|
||||
current_order = self._current_config.get("auth_order")
|
||||
if current_order != auth_order:
|
||||
self._commands.append('aaa authorization map order {0}' .format(auth_order))
|
||||
|
||||
auth_fallback_enabled = self._required_config.get("auth_fallback_enabled")
|
||||
if auth_fallback_enabled is not None:
|
||||
current_fallback = self._current_config.get("auth_fallback_enabled")
|
||||
if current_fallback != auth_fallback_enabled:
|
||||
if auth_fallback_enabled is True:
|
||||
self._commands.append('aaa authorization map fallback server-err')
|
||||
else:
|
||||
self._commands.append('no aaa authorization map fallback server-err')
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxAAAModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
244
plugins/modules/network/onyx/onyx_bfd.py
Normal file
244
plugins/modules/network/onyx/onyx_bfd.py
Normal file
@@ -0,0 +1,244 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_bfd
|
||||
author: "Sara Touqan (@sarato)"
|
||||
short_description: Configures BFD parameters
|
||||
description:
|
||||
- This module provides declarative management of BFD protocol params
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
shutdown:
|
||||
description:
|
||||
- Administratively shut down BFD protection.
|
||||
type: bool
|
||||
vrf:
|
||||
description:
|
||||
- Specifys the vrf name.
|
||||
type: str
|
||||
interval_min_rx:
|
||||
description:
|
||||
- Minimum desired receive rate, should be between 50 and 6000.
|
||||
type: int
|
||||
interval_multiplier:
|
||||
description:
|
||||
- Desired detection multiplier, should be between 3 and 50.
|
||||
type: int
|
||||
interval_transmit_rate:
|
||||
description:
|
||||
- Minimum desired transmit rate, should be between 50 and 60000.
|
||||
type: int
|
||||
iproute_network_prefix:
|
||||
description:
|
||||
- Configures the ip route network prefix, e.g 1.1.1.1.
|
||||
type: str
|
||||
iproute_mask_length:
|
||||
description:
|
||||
- Configures the mask length of the ip route network prefix, e.g 24.
|
||||
type: int
|
||||
iproute_next_hop:
|
||||
description:
|
||||
- Configures the ip route next hop, e.g 2.2.2.2.
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configures bfd
|
||||
onyx_bfd:
|
||||
shutdown: yes
|
||||
vrf: 5
|
||||
interval_min_rx: 55
|
||||
interval_multiplier: 8
|
||||
interval_transmit_rate: 88
|
||||
iproute_network_prefix: 1.1.1.0
|
||||
iproute_mask_length: 24
|
||||
iproute_next_hop: 3.2.2.2
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- ip bfd shutdown
|
||||
- no ip bfd shutdown
|
||||
- ip bfd shutdown vrf <vrf_name>
|
||||
- no ip bfd shutdown vrf <vrf_name>
|
||||
- ip bfd vrf <vrf_name> interval min-rx <min_rx> multiplier <multiplier> transmit-rate <transmit_rate> force
|
||||
- ip bfd interval min-rx <min_rx> multiplier <multiplier> transmit-rate <transmit_rate> force
|
||||
- ip route vrf <vrf_name> <network_prefix>/<mask_length> <next_hop> bfd
|
||||
- ip route <network_prefix>/<mask_length> <next_hop> bfd
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxBFDModule(BaseOnyxModule):
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
element_spec = dict(
|
||||
shutdown=dict(type='bool'),
|
||||
vrf=dict(type='str'),
|
||||
interval_min_rx=dict(type='int'),
|
||||
interval_multiplier=dict(type='int'),
|
||||
interval_transmit_rate=dict(type='int'),
|
||||
iproute_network_prefix=dict(type='str'),
|
||||
iproute_mask_length=dict(type='int'),
|
||||
iproute_next_hop=dict(type='str'),
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_together=[
|
||||
['interval_min_rx', 'interval_multiplier', 'interval_transmit_rate'],
|
||||
['iproute_network_prefix', 'iproute_mask_length', 'iproute_next_hop']])
|
||||
|
||||
def validate_bfd_interval_values(self):
|
||||
interval_min_rx = self._required_config.get('interval_min_rx')
|
||||
if interval_min_rx:
|
||||
if ((interval_min_rx < 50) or (interval_min_rx > 6000)):
|
||||
self._module.fail_json(msg='Receive interval should be between 50 and 6000.')
|
||||
interval_multiplier = self._required_config.get('interval_multiplier')
|
||||
if interval_multiplier:
|
||||
if ((interval_multiplier < 3) or (interval_multiplier > 50)):
|
||||
self._module.fail_json(msg='Multiplier should be between 3 and 50.')
|
||||
interval_transmit_rate = self._required_config.get('interval_transmit_rate')
|
||||
if interval_transmit_rate:
|
||||
if ((interval_transmit_rate < 50) or (interval_transmit_rate > 60000)):
|
||||
self._module.fail_json(msg='Transmit interval should be between 50 and 60000.')
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
self.validate_bfd_interval_values()
|
||||
|
||||
def _set_bfd_config(self, bfd_config):
|
||||
curr_config_arr = []
|
||||
bfd_config = bfd_config.get('Lines')
|
||||
if bfd_config is None:
|
||||
return
|
||||
for runn_config in bfd_config:
|
||||
curr_config_arr.append(runn_config.strip())
|
||||
if 'ip bfd shutdown vrf default' in curr_config_arr:
|
||||
self._current_config['bfd_shutdown'] = True
|
||||
else:
|
||||
self._current_config['bfd_shutdown'] = False
|
||||
self._current_config['curr_config_arr'] = curr_config_arr
|
||||
|
||||
def _show_bfd_config(self):
|
||||
cmd = "show running-config | include bfd"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
bfd_config = self._show_bfd_config()
|
||||
if bfd_config:
|
||||
self._set_bfd_config(bfd_config)
|
||||
|
||||
def generate_shutdown_commands(self, curr_config_arr):
|
||||
shutdown_enabled = self._required_config.get('shutdown')
|
||||
vrf_name = self._required_config.get('vrf')
|
||||
current_shutdown = self._current_config.get("bfd_shutdown")
|
||||
if shutdown_enabled is not None:
|
||||
if vrf_name is not None:
|
||||
if curr_config_arr is not None:
|
||||
if ('ip bfd shutdown vrf {0}' .format(vrf_name)) not in curr_config_arr:
|
||||
if shutdown_enabled is True:
|
||||
self._commands.append('ip bfd shutdown vrf {0}' .format(vrf_name))
|
||||
else:
|
||||
if shutdown_enabled is False:
|
||||
self._commands.append('no ip bfd shutdown vrf {0}' .format(vrf_name))
|
||||
else:
|
||||
if ((current_shutdown is not None and (current_shutdown != shutdown_enabled)) or (current_shutdown is None)):
|
||||
if shutdown_enabled is True:
|
||||
self._commands.append('ip bfd shutdown')
|
||||
else:
|
||||
self._commands.append('no ip bfd shutdown')
|
||||
|
||||
def generate_interval_commands(self, curr_config_arr):
|
||||
interval_min_rx = self._required_config.get('interval_min_rx')
|
||||
interval_multiplier = self._required_config.get('interval_multiplier')
|
||||
interval_transmit_rate = self._required_config.get('interval_transmit_rate')
|
||||
vrf_name = self._required_config.get('vrf')
|
||||
if ((interval_min_rx is not None) and (interval_multiplier is not None) and (interval_transmit_rate is not None)):
|
||||
if vrf_name is not None:
|
||||
if curr_config_arr is not None:
|
||||
if ((('ip bfd vrf {0} interval transmit-rate {1} force' .format(vrf_name, interval_transmit_rate)) not in curr_config_arr) or
|
||||
(('ip bfd vrf {0} interval min-rx {1} force' .format(vrf_name, interval_min_rx)) not in curr_config_arr) or
|
||||
(('ip bfd vrf {0} interval multiplier {1} force' .format(vrf_name, interval_multiplier)) not in curr_config_arr)):
|
||||
self._commands.append('ip bfd vrf {0} interval min-rx {1} multiplier {2} transmit-rate {3} force'
|
||||
.format(vrf_name, interval_min_rx, interval_multiplier, interval_transmit_rate))
|
||||
else:
|
||||
self._commands.append('ip bfd vrf {0} interval min-rx {1} multiplier {2} transmit-rate {3} force'
|
||||
.format(vrf_name, interval_min_rx, interval_multiplier, interval_transmit_rate))
|
||||
else:
|
||||
if curr_config_arr is not None:
|
||||
if ((('ip bfd vrf default interval transmit-rate {0} force' .format(interval_transmit_rate)) not in curr_config_arr) or
|
||||
(('ip bfd vrf default interval min-rx {0} force' .format(interval_min_rx)) not in curr_config_arr) or
|
||||
(('ip bfd vrf default interval multiplier {0} force' .format(interval_multiplier)) not in curr_config_arr)):
|
||||
self._commands.append('ip bfd interval min-rx {0} multiplier {1} transmit-rate {2} force'
|
||||
.format(interval_min_rx, interval_multiplier, interval_transmit_rate))
|
||||
else:
|
||||
self._commands.append('ip bfd interval min-rx {0} multiplier {1} transmit-rate {2} force'
|
||||
.format(interval_min_rx, interval_multiplier, interval_transmit_rate))
|
||||
|
||||
def generate_iproute_commands(self, curr_config_arr):
|
||||
iproute_network_prefix = self._required_config.get('iproute_network_prefix')
|
||||
iproute_mask_length = self._required_config.get('iproute_mask_length')
|
||||
iproute_next_hop = self._required_config.get('iproute_next_hop')
|
||||
vrf_name = self._required_config.get('vrf')
|
||||
if ((iproute_network_prefix is not None) and (iproute_mask_length is not None) and
|
||||
(iproute_next_hop is not None)):
|
||||
if vrf_name is not None:
|
||||
if curr_config_arr is not None:
|
||||
if ('ip route vrf {0} {1}/{2} {3} bfd' .format(vrf_name, iproute_network_prefix,
|
||||
iproute_mask_length, iproute_next_hop)) not in curr_config_arr:
|
||||
self._commands.append('ip route vrf {0} {1} /{2} {3} bfd'
|
||||
.format(vrf_name, iproute_network_prefix, iproute_mask_length, iproute_next_hop))
|
||||
else:
|
||||
self._commands.append('ip route vrf {0} {1} /{2} {3} bfd' .format(vrf_name, iproute_network_prefix, iproute_mask_length, iproute_next_hop))
|
||||
else:
|
||||
if curr_config_arr is not None:
|
||||
if ('ip route vrf default {0}/{1} {2} bfd' .format(iproute_network_prefix,
|
||||
iproute_mask_length, iproute_next_hop)) not in curr_config_arr:
|
||||
self._commands.append('ip route {0} /{1} {2} bfd' .format(iproute_network_prefix, iproute_mask_length, iproute_next_hop))
|
||||
else:
|
||||
self._commands.append('ip route {0} /{1} {2} bfd' .format(iproute_network_prefix, iproute_mask_length, iproute_next_hop))
|
||||
|
||||
def generate_commands(self):
|
||||
curr_config_arr = self._current_config.get("curr_config_arr")
|
||||
self.generate_shutdown_commands(curr_config_arr)
|
||||
self.generate_interval_commands(curr_config_arr)
|
||||
self.generate_iproute_commands(curr_config_arr)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxBFDModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
450
plugins/modules/network/onyx/onyx_bgp.py
Normal file
450
plugins/modules/network/onyx/onyx_bgp.py
Normal file
@@ -0,0 +1,450 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_bgp
|
||||
author: "Samer Deeb (@samerd), Anas Badaha (@anasb)"
|
||||
short_description: Configures BGP on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of BGP router and neighbors
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.4000
|
||||
options:
|
||||
as_number:
|
||||
description:
|
||||
- Local AS number.
|
||||
required: true
|
||||
router_id:
|
||||
description:
|
||||
- Router IP address.
|
||||
neighbors:
|
||||
description:
|
||||
- List of neighbors. Required if I(state=present).
|
||||
suboptions:
|
||||
remote_as:
|
||||
description:
|
||||
- Remote AS number.
|
||||
required: true
|
||||
neighbor:
|
||||
description:
|
||||
- Neighbor IP address.
|
||||
required: true
|
||||
multihop:
|
||||
description:
|
||||
- multihop number.
|
||||
networks:
|
||||
description:
|
||||
- List of advertised networks.
|
||||
fast_external_fallover:
|
||||
description:
|
||||
- will configure fast_external_fallover when it is True.
|
||||
type: bool
|
||||
max_paths:
|
||||
description:
|
||||
- Maximum bgp paths.
|
||||
ecmp_bestpath:
|
||||
description:
|
||||
- Enables ECMP across AS paths.
|
||||
type: bool
|
||||
evpn:
|
||||
description:
|
||||
- Configure evpn peer-group.
|
||||
type: bool
|
||||
vrf:
|
||||
description:
|
||||
- vrf name.
|
||||
state:
|
||||
description:
|
||||
- BGP state.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
purge:
|
||||
description:
|
||||
- will remove all neighbors when it is True.
|
||||
type: bool
|
||||
default: false
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure bgp
|
||||
onyx_bgp:
|
||||
as_number: 320
|
||||
router_id: 10.3.3.3
|
||||
neighbors:
|
||||
- remote_as: 321
|
||||
neighbor: 10.3.3.4
|
||||
- remote_as: 322
|
||||
neighbor: 10.3.3.5
|
||||
multihop: 250
|
||||
purge: True
|
||||
state: present
|
||||
networks:
|
||||
- 172.16.1.0/24
|
||||
vrf: default
|
||||
evpn: yes
|
||||
fast_external_fallover: yes
|
||||
max_paths: 32
|
||||
ecmp_bestpath: yes
|
||||
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- router bgp 320 vrf default
|
||||
- exit
|
||||
- router bgp 320 router-id 10.3.3.3 force
|
||||
- router bgp 320 vrf default bgp fast-external-fallover
|
||||
- router bgp 320 vrf default maximum-paths 32
|
||||
- router bgp 320 vrf default bestpath as-path multipath-relax force
|
||||
- router bgp 320 vrf default neighbor evpn peer-group
|
||||
- router bgp 320 vrf default neighbor evpn send-community extended
|
||||
- router bgp 320 vrf default address-family l2vpn-evpn neighbor evpn next-hop-unchanged
|
||||
- router bgp 320 vrf default address-family l2vpn-evpn neighbor evpn activate
|
||||
- router bgp 320 vrf default address-family l2vpn-evpn auto-create
|
||||
- router bgp 320 vrf default neighbor 10.3.3.4 remote-as 321
|
||||
- router bgp 320 vrf default neighbor 10.3.3.4 ebgp-multihop 250
|
||||
- router bgp 320 vrf default neighbor 10.3.3.5 remote-as 322
|
||||
- router bgp 320 vrf default network 172.16.1.0 /24
|
||||
"""
|
||||
import re
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_bgp_summary
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxBgpModule(BaseOnyxModule):
|
||||
LOCAL_AS_REGEX = re.compile(r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+).*')
|
||||
ROUTER_ID_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+).*router-id\s+(\S+)\s+.*')
|
||||
NEIGHBOR_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+).*neighbor\s+(\S+)\s+remote\-as\s+(\d+).*')
|
||||
NEIGHBOR_MULTIHOP_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+).*neighbor\s+(\S+)\s+ebgp\-multihop\s+(\d+).*')
|
||||
NETWORK_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+).*network\s+(\S+)\s+(\S+).*')
|
||||
FAST_EXTERNAL_FALLOVER_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+bgp fast\-external\-fallover.*')
|
||||
MAX_PATHS_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+maximum\-paths\s+(\d+).*')
|
||||
ECMP_BESTPATH_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+bestpath as\-path multipath\-relax.*')
|
||||
NEIGHBOR_EVPN_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+neighbor\s+(\S+)\s+peer\-group evpn.*')
|
||||
EVPN_PEER_GROUP_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+neighbor evpn peer\-group.*')
|
||||
EVPN_SEND_COMMUNITY_EXTENDED_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+neighbor evpn send-community extended.*')
|
||||
EVPN_NEXT_HOP_UNCHANGED_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+address\-family l2vpn\-evpn neighbor evpn next\-hop-unchanged.*')
|
||||
EVPN_ACTIVATE_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+address-family l2vpn\-evpn neighbor evpn activate.*')
|
||||
EVPN_AUTO_CREATE_REGEX = re.compile(
|
||||
r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+address-family l2vpn\-evpn auto-create.*')
|
||||
|
||||
_purge = False
|
||||
|
||||
EVPN_PEER_GROUP_ATTR = "evpn_peer_group"
|
||||
EVPN_SEND_COMMUNITY_EXTENDED_ATTR = "evpn_send_community_extended"
|
||||
EVPN_NEXT_HOP_UNCHANGED_ATTR = "evpn_next_hop_unchanged"
|
||||
EVPN_ACTIVATE_ATTR = "evpn_activate"
|
||||
EVPN_AUTO_CREATE_ATTR = "evpn_auto_create"
|
||||
|
||||
EVPN_PEER_GROUP_CMD = "router bgp %s vrf %s neighbor evpn peer-group"
|
||||
EVPN_SEND_COMMUNITY_EXTENDED_CMD = "router bgp %s vrf %s neighbor evpn send-community extended"
|
||||
EVPN_NEXT_HOP_UNCHANGED_CMD = "router bgp %s vrf %s address-family l2vpn-evpn neighbor evpn next-hop-unchanged"
|
||||
EVPN_ACTIVATE_CMD = "router bgp %s vrf %s address-family l2vpn-evpn neighbor evpn activate"
|
||||
EVPN_AUTO_CREATE_CMD = "router bgp %s vrf %s address-family l2vpn-evpn auto-create"
|
||||
|
||||
EVPN_ENABLE_ATTRS = [EVPN_PEER_GROUP_ATTR, EVPN_SEND_COMMUNITY_EXTENDED_ATTR,
|
||||
EVPN_NEXT_HOP_UNCHANGED_ATTR, EVPN_ACTIVATE_ATTR, EVPN_AUTO_CREATE_ATTR]
|
||||
|
||||
EVPN_DISABLE_ATTRS = [EVPN_PEER_GROUP_ATTR, EVPN_AUTO_CREATE_ATTR]
|
||||
|
||||
EVPN_COMMANDS_REGEX_MAPPER = {
|
||||
EVPN_PEER_GROUP_ATTR: (EVPN_PEER_GROUP_REGEX, EVPN_PEER_GROUP_CMD),
|
||||
EVPN_SEND_COMMUNITY_EXTENDED_ATTR: (EVPN_SEND_COMMUNITY_EXTENDED_REGEX,
|
||||
EVPN_SEND_COMMUNITY_EXTENDED_CMD),
|
||||
EVPN_NEXT_HOP_UNCHANGED_ATTR: (EVPN_NEXT_HOP_UNCHANGED_REGEX,
|
||||
EVPN_NEXT_HOP_UNCHANGED_CMD),
|
||||
EVPN_ACTIVATE_ATTR: (EVPN_ACTIVATE_REGEX, EVPN_ACTIVATE_CMD),
|
||||
EVPN_AUTO_CREATE_ATTR: (EVPN_AUTO_CREATE_REGEX, EVPN_AUTO_CREATE_CMD)
|
||||
}
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
neighbor_spec = dict(
|
||||
remote_as=dict(type='int', required=True),
|
||||
neighbor=dict(required=True),
|
||||
multihop=dict(type='int')
|
||||
)
|
||||
element_spec = dict(
|
||||
as_number=dict(type='int', required=True),
|
||||
router_id=dict(),
|
||||
neighbors=dict(type='list', elements='dict',
|
||||
options=neighbor_spec),
|
||||
networks=dict(type='list', elements='str'),
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
purge=dict(default=False, type='bool'),
|
||||
vrf=dict(),
|
||||
fast_external_fallover=dict(type='bool'),
|
||||
max_paths=dict(type='int'),
|
||||
ecmp_bestpath=dict(type='bool'),
|
||||
evpn=dict(type='bool')
|
||||
)
|
||||
argument_spec = dict()
|
||||
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self._purge = self._required_config.get('purge', False)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _set_bgp_config(self, bgp_config):
|
||||
lines = bgp_config.split('\n')
|
||||
self._current_config['router_id'] = None
|
||||
self._current_config['as_number'] = None
|
||||
self._current_config['fast_external_fallover'] = False
|
||||
self._current_config['ecmp_bestpath'] = False
|
||||
self._current_config[self.EVPN_PEER_GROUP_ATTR] = False
|
||||
self._current_config[self.EVPN_SEND_COMMUNITY_EXTENDED_ATTR] = False
|
||||
self._current_config[self.EVPN_NEXT_HOP_UNCHANGED_ATTR] = False
|
||||
self._current_config[self.EVPN_AUTO_CREATE_ATTR] = False
|
||||
self._current_config[self.EVPN_ACTIVATE_ATTR] = False
|
||||
neighbors = self._current_config['neighbors'] = dict()
|
||||
networks = self._current_config['networks'] = list()
|
||||
for line in lines:
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
if not self._current_config['as_number']:
|
||||
match = self.LOCAL_AS_REGEX.match(line)
|
||||
if match:
|
||||
self._current_config['as_number'] = int(match.group(1))
|
||||
self._current_config['vrf'] = match.group(2)
|
||||
continue
|
||||
if not self._current_config['router_id']:
|
||||
match = self.ROUTER_ID_REGEX.match(line)
|
||||
if match:
|
||||
self._current_config['router_id'] = match.group(2)
|
||||
continue
|
||||
match = self.NEIGHBOR_REGEX.match(line)
|
||||
if match:
|
||||
neighbor = neighbors.setdefault(match.group(2), dict())
|
||||
neighbor['remote_as'] = int(match.group(3))
|
||||
continue
|
||||
match = self.NEIGHBOR_MULTIHOP_REGEX.match(line)
|
||||
if match:
|
||||
neighbor = neighbors.setdefault(match.group(2), dict())
|
||||
neighbor["multihop"] = int(match.group(3))
|
||||
continue
|
||||
match = self.NEIGHBOR_EVPN_REGEX.match(line)
|
||||
if match:
|
||||
neighbor = neighbors.setdefault(match.group(3), dict())
|
||||
neighbor["evpn"] = True
|
||||
continue
|
||||
match = self.NETWORK_REGEX.match(line)
|
||||
if match:
|
||||
network = match.group(2) + match.group(3)
|
||||
networks.append(network)
|
||||
continue
|
||||
match = self.FAST_EXTERNAL_FALLOVER_REGEX.match(line)
|
||||
if match:
|
||||
self._current_config['fast_external_fallover'] = True
|
||||
continue
|
||||
match = self.ECMP_BESTPATH_REGEX.match(line)
|
||||
if match:
|
||||
self._current_config['ecmp_bestpath'] = True
|
||||
continue
|
||||
match = self.MAX_PATHS_REGEX.match(line)
|
||||
if match:
|
||||
self._current_config['max_paths'] = int(match.group(3))
|
||||
continue
|
||||
for key, value in iteritems(self.EVPN_COMMANDS_REGEX_MAPPER):
|
||||
match = value[0].match(line)
|
||||
if match:
|
||||
self._current_config[key] = True
|
||||
break
|
||||
|
||||
def _get_bgp_summary(self):
|
||||
return get_bgp_summary(self._module)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
bgp_config = self._get_bgp_summary()
|
||||
if bgp_config:
|
||||
self._set_bgp_config(bgp_config)
|
||||
|
||||
def generate_commands(self):
|
||||
state = self._required_config['state']
|
||||
if state == 'present':
|
||||
self._generate_bgp_cmds()
|
||||
else:
|
||||
self._generate_no_bgp_cmds()
|
||||
|
||||
def _generate_bgp_cmds(self):
|
||||
vrf = self._required_config.get('vrf')
|
||||
if vrf is None:
|
||||
vrf = "default"
|
||||
|
||||
as_number = self._required_config['as_number']
|
||||
curr_as_num = self._current_config.get('as_number')
|
||||
curr_vrf = self._current_config.get("vrf")
|
||||
bgp_removed = False
|
||||
if curr_as_num != as_number or vrf != curr_vrf:
|
||||
if curr_as_num:
|
||||
self._commands.append('no router bgp %d vrf %s' % (curr_as_num, curr_vrf))
|
||||
bgp_removed = True
|
||||
self._commands.append('router bgp %d vrf %s' % (as_number, vrf))
|
||||
self._commands.append('exit')
|
||||
|
||||
req_router_id = self._required_config.get('router_id')
|
||||
if req_router_id is not None:
|
||||
curr_route_id = self._current_config.get('router_id')
|
||||
if bgp_removed or req_router_id != curr_route_id:
|
||||
self._commands.append('router bgp %d vrf %s router-id %s force' % (as_number, vrf, req_router_id))
|
||||
|
||||
fast_external_fallover = self._required_config.get('fast_external_fallover')
|
||||
if fast_external_fallover is not None:
|
||||
current_fast_external_fallover = self._current_config.get('fast_external_fallover')
|
||||
if fast_external_fallover and (bgp_removed or fast_external_fallover != current_fast_external_fallover):
|
||||
self._commands.append('router bgp %d vrf %s bgp fast-external-fallover' % (as_number, vrf))
|
||||
elif not fast_external_fallover and (bgp_removed or fast_external_fallover != current_fast_external_fallover):
|
||||
self._commands.append('router bgp %d vrf %s no bgp fast-external-fallover' % (as_number, vrf))
|
||||
|
||||
max_paths = self._required_config.get('max_paths')
|
||||
if max_paths is not None:
|
||||
current_max_paths = self._current_config.get('max_paths')
|
||||
if bgp_removed or max_paths != current_max_paths:
|
||||
self._commands.append('router bgp %d vrf %s maximum-paths %s' % (as_number, vrf, max_paths))
|
||||
|
||||
ecmp_bestpath = self._required_config.get('ecmp_bestpath')
|
||||
if ecmp_bestpath is not None:
|
||||
current_ecmp_bestpath = self._current_config.get('ecmp_bestpath')
|
||||
if ecmp_bestpath and (bgp_removed or ecmp_bestpath != current_ecmp_bestpath):
|
||||
self._commands.append('router bgp %d vrf %s bestpath as-path multipath-relax force' % (as_number, vrf))
|
||||
elif not ecmp_bestpath and (bgp_removed or ecmp_bestpath != current_ecmp_bestpath):
|
||||
self._commands.append('router bgp %d vrf %s no bestpath as-path multipath-relax force' % (as_number, vrf))
|
||||
|
||||
evpn = self._required_config.get('evpn')
|
||||
if evpn is not None:
|
||||
self._generate_evpn_cmds(evpn, as_number, vrf)
|
||||
|
||||
self._generate_neighbors_cmds(as_number, vrf, bgp_removed)
|
||||
self._generate_networks_cmds(as_number, vrf, bgp_removed)
|
||||
|
||||
def _generate_neighbors_cmds(self, as_number, vrf, bgp_removed):
|
||||
req_neighbors = self._required_config['neighbors']
|
||||
curr_neighbors = self._current_config.get('neighbors', {})
|
||||
evpn = self._required_config.get('evpn')
|
||||
if self._purge:
|
||||
for neighbor in curr_neighbors:
|
||||
remote_as = curr_neighbors[neighbor].get("remote_as")
|
||||
self._commands.append('router bgp %s vrf %s no neighbor %s remote-as %s' % (
|
||||
as_number, vrf, neighbor, remote_as))
|
||||
|
||||
if req_neighbors is not None:
|
||||
for neighbor_data in req_neighbors:
|
||||
neighbor = neighbor_data.get("neighbor")
|
||||
curr_neighbor = curr_neighbors.get(neighbor)
|
||||
remote_as = neighbor_data.get("remote_as")
|
||||
multihop = neighbor_data.get("multihop")
|
||||
if bgp_removed or curr_neighbor is None:
|
||||
if remote_as is not None:
|
||||
self._commands.append(
|
||||
'router bgp %s vrf %s neighbor %s remote-as %s' % (as_number, vrf, neighbor, remote_as))
|
||||
if multihop is not None:
|
||||
self._commands.append(
|
||||
'router bgp %s vrf %s neighbor %s ebgp-multihop %s' % (as_number, vrf, neighbor, multihop))
|
||||
if evpn:
|
||||
self._commands.append(
|
||||
'router bgp %s vrf %s neighbor %s peer-group evpn' % (as_number, vrf, neighbor))
|
||||
elif curr_neighbor is not None:
|
||||
curr_remote_as = curr_neighbor.get("remote_as")
|
||||
curr_multihop = curr_neighbor.get("multihop")
|
||||
curr_neighbor_evpn = curr_neighbor.get("evpn")
|
||||
if remote_as != curr_remote_as:
|
||||
self._commands.append(
|
||||
'router bgp %s vrf %s neighbor %s remote-as %s' % (as_number, vrf, neighbor, remote_as))
|
||||
if multihop is not None and multihop != curr_multihop:
|
||||
self._commands.append(
|
||||
'router bgp %s vrf %s neighbor %s ebgp-multihop %s' % (as_number, vrf, neighbor, multihop))
|
||||
if evpn and curr_neighbor_evpn is not True:
|
||||
self._commands.append(
|
||||
'router bgp %s vrf %s neighbor %s peer-group evpn' % (as_number, vrf, neighbor))
|
||||
|
||||
def _generate_networks_cmds(self, as_number, vrf, bgp_removed):
|
||||
req_networks = self._required_config['networks'] or []
|
||||
curr_networks = self._current_config.get('networks', [])
|
||||
if not bgp_removed:
|
||||
for network in curr_networks:
|
||||
if network not in req_networks:
|
||||
net_attrs = network.split('/')
|
||||
if len(net_attrs) != 2:
|
||||
self._module.fail_json(
|
||||
msg='Invalid network %s' % network)
|
||||
|
||||
net_address, netmask = net_attrs
|
||||
cmd = 'router bgp %s no network %s /%s' % (
|
||||
as_number, net_address, netmask)
|
||||
self._commands.append(cmd)
|
||||
|
||||
for network in req_networks:
|
||||
if bgp_removed or network not in curr_networks:
|
||||
net_attrs = network.split('/')
|
||||
if len(net_attrs) != 2:
|
||||
self._module.fail_json(
|
||||
msg='Invalid network %s' % network)
|
||||
net_address, netmask = net_attrs
|
||||
cmd = 'router bgp %s vrf %s network %s /%s' % (
|
||||
as_number, vrf, net_address, netmask)
|
||||
self._commands.append(cmd)
|
||||
|
||||
def _generate_no_bgp_cmds(self):
|
||||
as_number = self._required_config['as_number']
|
||||
curr_as_num = self._current_config.get('as_number')
|
||||
if curr_as_num and curr_as_num == as_number:
|
||||
self._commands.append('no router bgp %d' % as_number)
|
||||
|
||||
def _generate_evpn_cmds(self, evpn, as_number, vrf):
|
||||
if evpn:
|
||||
for attr in self.EVPN_ENABLE_ATTRS:
|
||||
curr_attr = self._current_config.get(attr)
|
||||
if curr_attr is not True:
|
||||
self._commands.append(self.EVPN_COMMANDS_REGEX_MAPPER.get(attr)[1] % (as_number, vrf))
|
||||
elif not evpn:
|
||||
for attr in self.EVPN_DISABLE_ATTRS:
|
||||
curr_attr = self._current_config.get(attr)
|
||||
if curr_attr is not False:
|
||||
self._commands.append("no " + self.EVPN_COMMANDS_REGEX_MAPPER.get(attr)[1] % (as_number, vrf))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxBgpModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
144
plugins/modules/network/onyx/onyx_buffer_pool.py
Normal file
144
plugins/modules/network/onyx/onyx_buffer_pool.py
Normal file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_buffer_pool
|
||||
author: "Anas Badaha (@anasb)"
|
||||
short_description: Configures Buffer Pool
|
||||
description:
|
||||
- This module provides declarative management of Onyx Buffer Pool configuration
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.8130
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- pool name.
|
||||
required: true
|
||||
pool_type:
|
||||
description:
|
||||
- pool type.
|
||||
choices: ['lossless', 'lossy']
|
||||
default: lossy
|
||||
memory_percent:
|
||||
description:
|
||||
- memory percent.
|
||||
switch_priority:
|
||||
description:
|
||||
- switch priority, range 1-7.
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure buffer pool
|
||||
onyx_buffer_pool:
|
||||
name: roce
|
||||
pool_type: lossless
|
||||
memory_percent: 50.00
|
||||
switch_priority: 3
|
||||
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- traffic pool roce type lossless
|
||||
- traffic pool roce memory percent 50.00
|
||||
- traffic pool roce map switch-priority 3
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxBufferPoolModule(BaseOnyxModule):
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
element_spec = dict(
|
||||
name=dict(type='str', required=True),
|
||||
pool_type=dict(choices=['lossless', 'lossy'], default='lossy'),
|
||||
memory_percent=dict(type='float'),
|
||||
switch_priority=dict(type='int')
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def validate_switch_priority(self, value):
|
||||
if value and not 0 <= int(value) <= 7:
|
||||
self._module.fail_json(msg='switch_priority value must be between 0 and 7')
|
||||
|
||||
def _set_traffic_pool_config(self, traffic_pool_config):
|
||||
if traffic_pool_config is None:
|
||||
return
|
||||
traffic_pool_config = traffic_pool_config.get(self._required_config.get('name'))
|
||||
self._current_config['pool_type'] = traffic_pool_config[0].get("Type")
|
||||
self._current_config['switch_priority'] = int(traffic_pool_config[0].get("Switch Priorities"))
|
||||
self._current_config['memory_percent'] = float(traffic_pool_config[0].get("Memory [%]"))
|
||||
|
||||
def _show_traffic_pool(self):
|
||||
cmd = "show traffic pool {0}".format(self._required_config.get("name"))
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
traffic_pool_config = self._show_traffic_pool()
|
||||
self._set_traffic_pool_config(traffic_pool_config)
|
||||
|
||||
def generate_commands(self):
|
||||
name = self._required_config.get("name")
|
||||
pool_type = self._required_config.get("pool_type")
|
||||
|
||||
if self._current_config is None:
|
||||
self._add_add_traffic_pool_cmds(name, pool_type)
|
||||
else:
|
||||
current_pool_type = self._current_config.get("pool_type")
|
||||
if pool_type != current_pool_type:
|
||||
self._add_add_traffic_pool_cmds(name, pool_type)
|
||||
|
||||
memory_percent = self._required_config.get("memory_percent")
|
||||
if memory_percent is not None:
|
||||
curr_memory_percent = self._current_config.get("memory_percent")
|
||||
if curr_memory_percent is None or memory_percent != curr_memory_percent:
|
||||
self._commands.append('traffic pool {0} memory percent {1}'.format(name, memory_percent))
|
||||
|
||||
switch_priority = self._required_config.get("switch_priority")
|
||||
if switch_priority is not None:
|
||||
curr_switch_priority = self._current_config.get("switch_priority")
|
||||
if curr_switch_priority is None or switch_priority != curr_switch_priority:
|
||||
self._commands.append('traffic pool {0} map switch-priority {1}'.format(name, switch_priority))
|
||||
|
||||
def _add_add_traffic_pool_cmds(self, name, pool_type):
|
||||
self._commands.append('traffic pool {0} type {1}'.format(name, pool_type))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxBufferPoolModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
216
plugins/modules/network/onyx/onyx_command.py
Normal file
216
plugins/modules/network/onyx/onyx_command.py
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_command
|
||||
extends_documentation_fragment:
|
||||
- community.general.onyx
|
||||
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Run commands on remote devices running Mellanox ONYX
|
||||
description:
|
||||
- Sends arbitrary commands to an Mellanox ONYX network device and returns
|
||||
the results read from the device. This module includes an
|
||||
argument that will cause the module to wait for a specific condition
|
||||
before returning or timing out if the condition is not met.
|
||||
- This module does not support running commands in configuration mode.
|
||||
Please use M(onyx_config) to configure Mellanox ONYX devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.4000
|
||||
options:
|
||||
commands:
|
||||
description:
|
||||
- List of commands to send to the remote Mellanox ONYX network device.
|
||||
The resulting output from the command
|
||||
is returned. If the I(wait_for) argument is provided, the
|
||||
module is not returned until the condition is satisfied or
|
||||
the number of retries has expired.
|
||||
required: true
|
||||
wait_for:
|
||||
description:
|
||||
- List of conditions to evaluate against the output of the
|
||||
command. The task will wait for each condition to be true
|
||||
before moving forward. If the conditional is not true
|
||||
within the configured number of retries, the task fails.
|
||||
See examples.
|
||||
match:
|
||||
description:
|
||||
- The I(match) argument is used in conjunction with the
|
||||
I(wait_for) argument to specify the match policy. Valid
|
||||
values are C(all) or C(any). If the value is set to C(all)
|
||||
then all conditionals in the wait_for must be satisfied. If
|
||||
the value is set to C(any) then only one of the values must be
|
||||
satisfied.
|
||||
default: all
|
||||
choices: ['any', 'all']
|
||||
retries:
|
||||
description:
|
||||
- Specifies the number of retries a command should by tried
|
||||
before it is considered failed. The command is run on the
|
||||
target device every retry and evaluated against the
|
||||
I(wait_for) conditions.
|
||||
default: 10
|
||||
interval:
|
||||
description:
|
||||
- Configures the interval in seconds to wait between retries
|
||||
of the command. If the command does not pass the specified
|
||||
conditions, the interval indicates how long to wait before
|
||||
trying the command again.
|
||||
default: 1
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
tasks:
|
||||
- name: run show version on remote devices
|
||||
onyx_command:
|
||||
commands: show version
|
||||
|
||||
- name: run show version and check to see if output contains MLNXOS
|
||||
onyx_command:
|
||||
commands: show version
|
||||
wait_for: result[0] contains MLNXOS
|
||||
|
||||
- name: run multiple commands on remote nodes
|
||||
onyx_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces
|
||||
|
||||
- name: run multiple commands and evaluate the output
|
||||
onyx_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces
|
||||
wait_for:
|
||||
- result[0] contains MLNXOS
|
||||
- result[1] contains mgmt1
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
stdout:
|
||||
description: The set of responses from the commands
|
||||
returned: always apart from low level errors (such as action plugin)
|
||||
type: list
|
||||
sample: ['...', '...']
|
||||
stdout_lines:
|
||||
description: The value of stdout split into a list
|
||||
returned: always apart from low level errors (such as action plugin)
|
||||
type: list
|
||||
sample: [['...', '...'], ['...'], ['...']]
|
||||
failed_conditions:
|
||||
description: The list of conditionals that have failed
|
||||
returned: failed
|
||||
type: list
|
||||
sample: ['...', '...']
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ComplexList
|
||||
from ansible.module_utils.six import string_types
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import run_commands
|
||||
|
||||
|
||||
def to_lines(stdout):
|
||||
for item in stdout:
|
||||
if isinstance(item, string_types):
|
||||
item = str(item).split('\n')
|
||||
yield item
|
||||
|
||||
|
||||
def parse_commands(module, warnings):
|
||||
command = ComplexList(dict(
|
||||
command=dict(key=True),
|
||||
prompt=dict(),
|
||||
answer=dict()
|
||||
), module)
|
||||
commands = command(module.params['commands'])
|
||||
for item in list(commands):
|
||||
if module.check_mode and not item['command'].startswith('show'):
|
||||
warnings.append(
|
||||
'only show commands are supported when using check mode, not '
|
||||
'executing `%s`' % item['command']
|
||||
)
|
||||
commands.remove(item)
|
||||
elif item['command'].startswith('conf'):
|
||||
module.fail_json(
|
||||
msg='onyx_command does not support running config mode '
|
||||
'commands. Please use onyx_config instead'
|
||||
)
|
||||
return commands
|
||||
|
||||
|
||||
def main():
|
||||
"""main entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
commands=dict(type='list', required=True),
|
||||
|
||||
wait_for=dict(type='list'),
|
||||
match=dict(default='all', choices=['all', 'any']),
|
||||
|
||||
retries=dict(default=10, type='int'),
|
||||
interval=dict(default=1, type='int')
|
||||
)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = {'changed': False}
|
||||
|
||||
warnings = list()
|
||||
commands = parse_commands(module, warnings)
|
||||
result['warnings'] = warnings
|
||||
|
||||
wait_for = module.params['wait_for'] or list()
|
||||
conditionals = [Conditional(c) for c in wait_for]
|
||||
|
||||
retries = module.params['retries']
|
||||
interval = module.params['interval']
|
||||
match = module.params['match']
|
||||
|
||||
while retries > 0:
|
||||
responses = run_commands(module, commands)
|
||||
|
||||
for item in list(conditionals):
|
||||
if item(responses):
|
||||
if match == 'any':
|
||||
conditionals = list()
|
||||
break
|
||||
conditionals.remove(item)
|
||||
|
||||
if not conditionals:
|
||||
break
|
||||
|
||||
time.sleep(interval)
|
||||
retries -= 1
|
||||
|
||||
if conditionals:
|
||||
failed_conditions = [item.raw for item in conditionals]
|
||||
msg = 'One or more conditional statements have not been satisfied'
|
||||
module.fail_json(msg=msg, failed_conditions=failed_conditions)
|
||||
|
||||
result.update({
|
||||
'changed': False,
|
||||
'stdout': responses,
|
||||
'stdout_lines': list(to_lines(responses))
|
||||
})
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
254
plugins/modules/network/onyx/onyx_config.py
Normal file
254
plugins/modules/network/onyx/onyx_config.py
Normal file
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_config
|
||||
extends_documentation_fragment:
|
||||
- community.general.onyx
|
||||
|
||||
author: "Alex Tabachnik (@atabachnik), Samer Deeb (@samerd)"
|
||||
short_description: Manage Mellanox ONYX configuration sections
|
||||
description:
|
||||
- Mellanox ONYX configurations uses a simple block indent file syntax
|
||||
for segmenting configuration into sections. This module provides
|
||||
an implementation for working with ONYX configuration sections in
|
||||
a deterministic way.
|
||||
options:
|
||||
lines:
|
||||
description:
|
||||
- The ordered set of commands that should be configured in the
|
||||
section. The commands must be the exact same commands as found
|
||||
in the device running-config. Be sure to note the configuration
|
||||
command syntax as some commands are automatically modified by the
|
||||
device config parser.
|
||||
aliases: ['commands']
|
||||
parents:
|
||||
description:
|
||||
- The ordered set of parents that uniquely identify the section
|
||||
the commands should be checked against. If the parents argument
|
||||
is omitted, the commands are checked against the set of top
|
||||
level or global commands.
|
||||
src:
|
||||
description:
|
||||
- Specifies the source path to the file that contains the configuration
|
||||
or configuration template to load. The path to the source file can
|
||||
either be the full path on the Ansible control host or a relative
|
||||
path from the playbook or role root directory. This argument is mutually
|
||||
exclusive with I(lines), I(parents).
|
||||
before:
|
||||
description:
|
||||
- The ordered set of commands to push on to the command stack if
|
||||
a change needs to be made. This allows the playbook designer
|
||||
the opportunity to perform configuration commands prior to pushing
|
||||
any changes without affecting how the set of commands are matched
|
||||
against the system.
|
||||
after:
|
||||
description:
|
||||
- The ordered set of commands to append to the end of the command
|
||||
stack if a change needs to be made. Just like with I(before) this
|
||||
allows the playbook designer to append a set of commands to be
|
||||
executed after the command set.
|
||||
match:
|
||||
description:
|
||||
- Instructs the module on the way to perform the matching of
|
||||
the set of commands against the current device config. If
|
||||
match is set to I(line), commands are matched line by line. If
|
||||
match is set to I(strict), command lines are matched with respect
|
||||
to position. If match is set to I(exact), command lines
|
||||
must be an equal match. Finally, if match is set to I(none), the
|
||||
module will not attempt to compare the source configuration with
|
||||
the running configuration on the remote device.
|
||||
default: line
|
||||
choices: ['line', 'strict', 'exact', 'none']
|
||||
replace:
|
||||
description:
|
||||
- Instructs the module on the way to perform the configuration
|
||||
on the device. If the replace argument is set to I(line) then
|
||||
the modified lines are pushed to the device in configuration
|
||||
mode. If the replace argument is set to I(block) then the entire
|
||||
command block is pushed to the device in configuration mode if any
|
||||
line is not correct
|
||||
default: line
|
||||
choices: ['line', 'block']
|
||||
backup:
|
||||
description:
|
||||
- This argument will cause the module to create a full backup of
|
||||
the current C(running-config) from the remote device before any
|
||||
changes are made. If the C(backup_options) value is not given,
|
||||
the backup file is written to the C(backup) folder in the playbook
|
||||
root directory. If the directory does not exist, it is created.
|
||||
default: no
|
||||
type: bool
|
||||
config:
|
||||
description:
|
||||
- The C(config) argument allows the playbook designer to supply
|
||||
the base configuration to be used to validate configuration
|
||||
changes necessary. If this argument is provided, the module
|
||||
will not download the running-config from the remote node.
|
||||
save:
|
||||
description:
|
||||
- The C(save) argument instructs the module to save the running-
|
||||
config to the startup-config at the conclusion of the module
|
||||
running. If check mode is specified, this argument is ignored.
|
||||
default: no
|
||||
type: bool
|
||||
backup_options:
|
||||
description:
|
||||
- This is a dict object containing configurable options related to backup file path.
|
||||
The value of this option is read only when C(backup) is set to I(yes), if C(backup) is set
|
||||
to I(no) this option will be silently ignored.
|
||||
suboptions:
|
||||
filename:
|
||||
description:
|
||||
- The filename to be used to store the backup configuration. If the filename
|
||||
is not given it will be generated based on the hostname, current time and date
|
||||
in format defined by <hostname>_config.<current-date>@<current-time>
|
||||
dir_path:
|
||||
description:
|
||||
- This option provides the path ending with directory name in which the backup
|
||||
configuration file will be stored. If the directory does not exist it will be first
|
||||
created and the filename is either the value of C(filename) or default filename
|
||||
as described in C(filename) options description. If the path value is not given
|
||||
in that case a I(backup) directory will be created in the current working directory
|
||||
and backup configuration will be copied in C(filename) within I(backup) directory.
|
||||
type: path
|
||||
type: dict
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
- onyx_config:
|
||||
lines:
|
||||
- snmp-server community
|
||||
- snmp-server host 10.2.2.2 traps version 2c
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
updates:
|
||||
description: The set of commands that will be pushed to the remote device
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['...', '...']
|
||||
backup_path:
|
||||
description: The full path to the backup file
|
||||
returned: when backup is yes
|
||||
type: str
|
||||
sample: /playbooks/ansible/backup/onyx_config.2016-07-16@22:28:34
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_config
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import load_config
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import run_commands
|
||||
|
||||
|
||||
def get_candidate(module):
|
||||
candidate = NetworkConfig(indent=1)
|
||||
if module.params['src']:
|
||||
candidate.load(module.params['src'])
|
||||
elif module.params['lines']:
|
||||
parents = module.params['parents'] or list()
|
||||
candidate.add(module.params['lines'], parents=parents)
|
||||
return candidate
|
||||
|
||||
|
||||
def run(module, result):
|
||||
match = module.params['match']
|
||||
replace = module.params['replace']
|
||||
path = module.params['parents']
|
||||
|
||||
candidate = get_candidate(module)
|
||||
if match != 'none':
|
||||
contents = module.params['config']
|
||||
if not contents:
|
||||
contents = get_config(module)
|
||||
config = NetworkConfig(indent=1, contents=contents)
|
||||
configobjs = candidate.difference(config, path=path, match=match,
|
||||
replace=replace)
|
||||
|
||||
else:
|
||||
configobjs = candidate.items
|
||||
|
||||
total_commands = []
|
||||
if configobjs:
|
||||
commands = dumps(configobjs, 'commands').split('\n')
|
||||
|
||||
if module.params['lines']:
|
||||
if module.params['before']:
|
||||
commands[:0] = module.params['before']
|
||||
|
||||
if module.params['after']:
|
||||
commands.extend(module.params['after'])
|
||||
|
||||
total_commands.extend(commands)
|
||||
result['updates'] = total_commands
|
||||
|
||||
if module.params['save']:
|
||||
total_commands.append('configuration write')
|
||||
if total_commands:
|
||||
result['changed'] = True
|
||||
if not module.check_mode:
|
||||
load_config(module, total_commands)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
backup_spec = dict(
|
||||
filename=dict(),
|
||||
dir_path=dict(type='path')
|
||||
)
|
||||
argument_spec = dict(
|
||||
src=dict(type='path'),
|
||||
|
||||
lines=dict(aliases=['commands'], type='list'),
|
||||
parents=dict(type='list'),
|
||||
|
||||
before=dict(type='list'),
|
||||
after=dict(type='list'),
|
||||
|
||||
match=dict(default='line', choices=['line', 'strict', 'exact', 'none']),
|
||||
replace=dict(default='line', choices=['line', 'block']),
|
||||
|
||||
config=dict(),
|
||||
|
||||
backup=dict(type='bool', default=False),
|
||||
backup_options=dict(type='dict', options=backup_spec),
|
||||
save=dict(type='bool', default=False),
|
||||
)
|
||||
|
||||
mutually_exclusive = [('lines', 'src'),
|
||||
('parents', 'src')]
|
||||
|
||||
required_if = [('match', 'strict', ['lines']),
|
||||
('match', 'exact', ['lines']),
|
||||
('replace', 'block', ['lines'])]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = {'changed': False}
|
||||
if module.params['backup']:
|
||||
result['__backup__'] = get_config(module)
|
||||
|
||||
run(module, result)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
245
plugins/modules/network/onyx/onyx_facts.py
Normal file
245
plugins/modules/network/onyx/onyx_facts.py
Normal file
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_facts
|
||||
author: "Waleed Mousa (@waleedym), Samer Deeb (@samerd)"
|
||||
short_description: Collect facts from Mellanox ONYX network devices
|
||||
description:
|
||||
- Collects a base set of device facts from a ONYX Mellanox network devices
|
||||
This module prepends all of the base network fact keys with
|
||||
C(ansible_net_<fact>). The facts module will always collect a base set of
|
||||
facts from the device and can enable or disable collection of additional
|
||||
facts.
|
||||
notes:
|
||||
- Tested against ONYX 3.6
|
||||
options:
|
||||
gather_subset:
|
||||
description:
|
||||
- When supplied, this argument will restrict the facts collected
|
||||
to a given subset. Possible values for this argument include
|
||||
all, version, module, and interfaces. Can specify a list of
|
||||
values to include a larger subset. Values can also be used
|
||||
with an initial C(M(!)) to specify that a specific subset should
|
||||
not be collected.
|
||||
required: false
|
||||
default: version
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Collect all facts from the device
|
||||
onyx_facts:
|
||||
gather_subset: all
|
||||
- name: Collect only the interfaces facts
|
||||
onyx_facts:
|
||||
gather_subset:
|
||||
- interfaces
|
||||
- name: Do not collect version facts
|
||||
onyx_facts:
|
||||
gather_subset:
|
||||
- "!version"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
ansible_net_gather_subset:
|
||||
description: The list of fact subsets collected from the device
|
||||
returned: always
|
||||
type: list
|
||||
# version
|
||||
ansible_net_version:
|
||||
description: A hash of all currently running system image information
|
||||
returned: when version is configured or when no gather_subset is provided
|
||||
type: dict
|
||||
# modules
|
||||
ansible_net_modules:
|
||||
description: A hash of all modules on the systeme with status
|
||||
returned: when modules is configured
|
||||
type: dict
|
||||
# interfaces
|
||||
ansible_net_interfaces:
|
||||
description: A hash of all interfaces running on the system
|
||||
returned: when interfaces is configured
|
||||
type: dict
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxFactsModule(BaseOnyxModule):
|
||||
|
||||
def get_runable_subset(self, gather_subset):
|
||||
runable_subsets = set()
|
||||
exclude_subsets = set()
|
||||
for subset in gather_subset:
|
||||
if subset == 'all':
|
||||
runable_subsets.update(VALID_SUBSETS)
|
||||
continue
|
||||
|
||||
if subset.startswith('!'):
|
||||
subset = subset[1:]
|
||||
if subset == 'all':
|
||||
exclude_subsets.update(VALID_SUBSETS)
|
||||
continue
|
||||
exclude = True
|
||||
else:
|
||||
exclude = False
|
||||
|
||||
if subset not in VALID_SUBSETS:
|
||||
self._module.fail_json(msg='Bad subset')
|
||||
|
||||
if exclude:
|
||||
exclude_subsets.add(subset)
|
||||
else:
|
||||
runable_subsets.add(subset)
|
||||
|
||||
if not runable_subsets:
|
||||
runable_subsets.update(VALID_SUBSETS)
|
||||
|
||||
runable_subsets.difference_update(exclude_subsets)
|
||||
if not runable_subsets:
|
||||
runable_subsets.add('version')
|
||||
return runable_subsets
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
argument_spec = dict(
|
||||
gather_subset=dict(default=['version'], type='list')
|
||||
)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def run(self):
|
||||
self.init_module()
|
||||
gather_subset = self._module.params['gather_subset']
|
||||
runable_subsets = self.get_runable_subset(gather_subset)
|
||||
facts = dict()
|
||||
facts['gather_subset'] = list(runable_subsets)
|
||||
|
||||
instances = list()
|
||||
for key in runable_subsets:
|
||||
facter_cls = FACT_SUBSETS[key]
|
||||
instances.append(facter_cls(self._module))
|
||||
|
||||
for inst in instances:
|
||||
inst.populate()
|
||||
facts.update(inst.facts)
|
||||
|
||||
ansible_facts = dict()
|
||||
for key, value in iteritems(facts):
|
||||
key = 'ansible_net_%s' % key
|
||||
ansible_facts[key] = value
|
||||
self._module.exit_json(ansible_facts=ansible_facts)
|
||||
|
||||
|
||||
class FactsBase(object):
|
||||
|
||||
COMMANDS = ['']
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.facts = dict()
|
||||
self.responses = None
|
||||
|
||||
def _show_cmd(self, cmd):
|
||||
return show_cmd(self.module, cmd, json_fmt=True)
|
||||
|
||||
def populate(self):
|
||||
self.responses = []
|
||||
for cmd in self.COMMANDS:
|
||||
self.responses.append(self._show_cmd(cmd))
|
||||
|
||||
|
||||
class Version(FactsBase):
|
||||
|
||||
COMMANDS = ['show version']
|
||||
|
||||
def populate(self):
|
||||
super(Version, self).populate()
|
||||
data = self.responses[0]
|
||||
if data:
|
||||
self.facts['version'] = data
|
||||
|
||||
|
||||
class Module(FactsBase):
|
||||
|
||||
COMMANDS = ['show module']
|
||||
|
||||
def populate(self):
|
||||
super(Module, self).populate()
|
||||
data = self.responses[0]
|
||||
if data:
|
||||
self.facts['modules'] = data
|
||||
|
||||
|
||||
class Interfaces(FactsBase):
|
||||
|
||||
COMMANDS = ['show version', 'show interfaces ethernet']
|
||||
|
||||
def populate(self):
|
||||
super(Interfaces, self).populate()
|
||||
|
||||
version_data = self.responses[0]
|
||||
os_version = version_data['Product release']
|
||||
data = self.responses[1]
|
||||
|
||||
if data:
|
||||
self.facts['interfaces'] = self.populate_interfaces(data, os_version)
|
||||
|
||||
def extractIfData(self, interface_data):
|
||||
return {"MAC Address": interface_data["Mac address"],
|
||||
"Actual Speed": interface_data["Actual speed"],
|
||||
"MTU": interface_data["MTU"],
|
||||
"Admin State": interface_data["Admin state"],
|
||||
"Operational State": interface_data["Operational state"]}
|
||||
|
||||
def populate_interfaces(self, interfaces, os_version):
|
||||
interfaces_dict = dict()
|
||||
for if_data in interfaces:
|
||||
if_dict = dict()
|
||||
if os_version >= BaseOnyxModule.ONYX_API_VERSION:
|
||||
for if_name, interface_data in iteritems(if_data):
|
||||
interface_data = interface_data[0]
|
||||
if_dict = self.extractIfData(interface_data)
|
||||
if_name = if_dict["Interface Name"] = if_name
|
||||
|
||||
else:
|
||||
if_dict = self.extractIfData(if_data)
|
||||
if_name = if_dict["Interface Name"] = if_data["header"]
|
||||
interfaces_dict[if_name] = if_dict
|
||||
return interfaces_dict
|
||||
|
||||
|
||||
FACT_SUBSETS = dict(
|
||||
version=Version,
|
||||
modules=Module,
|
||||
interfaces=Interfaces
|
||||
)
|
||||
|
||||
VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxFactsModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
224
plugins/modules/network/onyx/onyx_igmp.py
Normal file
224
plugins/modules/network/onyx/onyx_igmp.py
Normal file
@@ -0,0 +1,224 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_igmp
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Configures IGMP global parameters
|
||||
description:
|
||||
- This module provides declarative management of IGMP protocol params
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.6107
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- IGMP state.
|
||||
required: true
|
||||
choices: ['enabled', 'disabled']
|
||||
last_member_query_interval:
|
||||
description:
|
||||
- Configure the last member query interval, range 1-25
|
||||
mrouter_timeout:
|
||||
description:
|
||||
- Configure the mrouter timeout, range 60-600
|
||||
port_purge_timeout:
|
||||
description:
|
||||
- Configure the host port purge timeout, range 130-1225
|
||||
proxy_reporting:
|
||||
description:
|
||||
- Configure ip igmp snooping proxy and enable reporting mode
|
||||
choices: ['enabled', 'disabled']
|
||||
report_suppression_interval:
|
||||
description:
|
||||
- Configure the report suppression interval, range 1-25
|
||||
unregistered_multicast:
|
||||
description:
|
||||
- Configure the unregistered multicast mode
|
||||
Flood unregistered multicast
|
||||
Forward unregistered multicast to mrouter ports
|
||||
choices: ['flood', 'forward-to-mrouter-ports']
|
||||
default_version:
|
||||
description:
|
||||
- Configure the default operating version of the IGMP snooping
|
||||
choices: ['V2','V3']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure igmp
|
||||
onyx_igmp:
|
||||
state: enabled
|
||||
unregistered_multicast: flood
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- ip igmp snooping
|
||||
- ip igmp snooping last-member-query-interval 10
|
||||
- ip igmp snooping mrouter-timeout 150
|
||||
- ip igmp snooping port-purge-timeout 150
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxIgmpModule(BaseOnyxModule):
|
||||
TIME_INTERVAL_REGEX = re.compile(r'^(\d+)\s+seconds')
|
||||
|
||||
_RANGE_INTERVALS = dict(
|
||||
last_member_query_interval=(1, 25, 'Last member query interval'),
|
||||
mrouter_timeout=(60, 600, 'Mrouter timeout'),
|
||||
port_purge_timeout=(130, 1225, 'Port purge timeout'),
|
||||
report_suppression_interval=(1, 25, 'Report suppression interval'),
|
||||
)
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
element_spec = dict(
|
||||
state=dict(choices=['enabled', 'disabled'], required=True),
|
||||
last_member_query_interval=dict(type='int'),
|
||||
mrouter_timeout=dict(type='int'),
|
||||
port_purge_timeout=dict(type='int'),
|
||||
proxy_reporting=dict(choices=['enabled', 'disabled']),
|
||||
report_suppression_interval=dict(type='int'),
|
||||
unregistered_multicast=dict(
|
||||
choices=['flood', 'forward-to-mrouter-ports']),
|
||||
default_version=dict(choices=['V2', 'V3']),
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def _validate_key(self, param, key):
|
||||
interval_params = self._RANGE_VALIDATORS.get(key)
|
||||
if interval_params:
|
||||
min_val, max_val = interval_params[0], interval_params[1]
|
||||
value = param.get(key)
|
||||
self._validate_range(key, min_val, max_val, value)
|
||||
else:
|
||||
super(OnyxIgmpModule, self)._validate_key(param, key)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _set_igmp_config(self, igmp_config):
|
||||
igmp_config = igmp_config[0]
|
||||
if not igmp_config:
|
||||
return
|
||||
self._current_config['state'] = igmp_config.get(
|
||||
'IGMP snooping globally', 'disabled')
|
||||
self._current_config['proxy_reporting'] = igmp_config.get(
|
||||
'Proxy-reporting globally', 'disabled')
|
||||
self._current_config['default_version'] = igmp_config.get(
|
||||
'IGMP default version for new VLAN', 'V3')
|
||||
self._current_config['unregistered_multicast'] = igmp_config.get(
|
||||
'IGMP snooping unregistered multicast', 'flood')
|
||||
|
||||
for interval_name, interval_params in iteritems(self._RANGE_INTERVALS):
|
||||
display_str = interval_params[2]
|
||||
value = igmp_config.get(display_str, '')
|
||||
match = self.TIME_INTERVAL_REGEX.match(value)
|
||||
if match:
|
||||
interval_value = int(match.group(1))
|
||||
else:
|
||||
interval_value = None
|
||||
self._current_config[interval_name] = interval_value
|
||||
|
||||
def _show_igmp(self):
|
||||
cmd = "show ip igmp snooping"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
igmp_config = self._show_igmp()
|
||||
if igmp_config:
|
||||
self._set_igmp_config(igmp_config)
|
||||
|
||||
def generate_commands(self):
|
||||
state = self._required_config['state']
|
||||
if state == 'enabled':
|
||||
self._generate_igmp_cmds()
|
||||
else:
|
||||
self._generate_no_igmp_cmds()
|
||||
|
||||
def _generate_igmp_cmds(self):
|
||||
curr_state = self._current_config.get('state', 'disabled')
|
||||
if curr_state == 'disabled':
|
||||
self._commands.append('ip igmp snooping')
|
||||
for interval_name in self._RANGE_INTERVALS:
|
||||
req_val = self._required_config.get(interval_name)
|
||||
if not req_val:
|
||||
continue
|
||||
curr_value = self._current_config.get(interval_name)
|
||||
if curr_value == req_val:
|
||||
continue
|
||||
interval_cmd = interval_name.replace('_', '-')
|
||||
self._commands.append(
|
||||
'ip igmp snooping %s %s' % (interval_cmd, req_val))
|
||||
|
||||
req_val = self._required_config.get('unregistered_multicast')
|
||||
if req_val:
|
||||
curr_value = self._current_config.get(
|
||||
'unregistered_multicast', 'flood')
|
||||
if req_val != curr_value:
|
||||
self._commands.append(
|
||||
'ip igmp snooping unregistered multicast %s' % req_val)
|
||||
|
||||
req_val = self._required_config.get('proxy_reporting')
|
||||
if req_val:
|
||||
curr_value = self._current_config.get(
|
||||
'proxy_reporting', 'disabled')
|
||||
if req_val != curr_value:
|
||||
cmd = 'ip igmp snooping proxy reporting'
|
||||
if req_val == 'disabled':
|
||||
cmd = 'no %s' % cmd
|
||||
self._commands.append(cmd)
|
||||
|
||||
req_val = self._required_config.get('default_version')
|
||||
if req_val:
|
||||
curr_value = self._current_config.get(
|
||||
'default_version', 'V3')
|
||||
if req_val != curr_value:
|
||||
version = req_val[1] # remove the 'V' and take the number only
|
||||
self._commands.append(
|
||||
'ip igmp snooping version %s' % version)
|
||||
|
||||
def _generate_no_igmp_cmds(self):
|
||||
curr_state = self._current_config.get('state', 'disabled')
|
||||
if curr_state != 'disabled':
|
||||
self._commands.append('no ip igmp snooping')
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxIgmpModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
135
plugins/modules/network/onyx/onyx_igmp_interface.py
Normal file
135
plugins/modules/network/onyx/onyx_igmp_interface.py
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_igmp_interface
|
||||
author: "Anas Badaha (@anasb)"
|
||||
short_description: Configures IGMP interface parameters
|
||||
description:
|
||||
- This module provides declarative management of IGMP interface configuration
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.8130
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- interface name that we want to configure IGMP on it
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- IGMP Interface state.
|
||||
choices: ['enabled', 'disabled']
|
||||
default: enabled
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure igmp interface
|
||||
onyx_igmp_interface:
|
||||
state: enabled
|
||||
name: Eth1/1
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- interface ethernet 1/1 ip igmp snooping fast-leave
|
||||
"""
|
||||
|
||||
import re
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxIgmpInterfaceModule(BaseOnyxModule):
|
||||
IF_NAME_REGEX = re.compile(r"^(Eth\d+\/\d+|Eth\d+\/\d+\d+)$")
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
element_spec = dict(
|
||||
state=dict(choices=['enabled', 'disabled'], default='enabled'),
|
||||
name=dict(required=True)
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
match = self.IF_NAME_REGEX.match(self._required_config["name"])
|
||||
if not match:
|
||||
raise AttributeError("Please Insert Valid Interface Name")
|
||||
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _set_igmp_config(self, igmp_interfaces_config):
|
||||
if not igmp_interfaces_config:
|
||||
return
|
||||
name = self._required_config.get('name')
|
||||
interface_state = igmp_interfaces_config[name][0].get('leave-mode')
|
||||
if interface_state == "Fast":
|
||||
self._current_config['state'] = "enabled"
|
||||
else:
|
||||
self._current_config['state'] = "disabled"
|
||||
|
||||
def _show_igmp_interfaces(self):
|
||||
cmd = "show ip igmp snooping interfaces"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
igmp_interfaces_config = self._show_igmp_interfaces()
|
||||
if igmp_interfaces_config:
|
||||
self._set_igmp_config(igmp_interfaces_config)
|
||||
|
||||
def generate_commands(self):
|
||||
req_state = self._required_config['state']
|
||||
self._req_val = self._required_config.get('name').replace("Eth", "ethernet ")
|
||||
|
||||
if req_state == 'enabled':
|
||||
self._generate_igmp_interface_cmds()
|
||||
else:
|
||||
self._generate_no_igmp_cmds()
|
||||
|
||||
def _generate_igmp_interface_cmds(self):
|
||||
curr_state = self._current_config.get('state', 'enabled')
|
||||
if curr_state == 'enabled':
|
||||
pass
|
||||
|
||||
elif curr_state == 'disabled':
|
||||
self._commands.append('interface %s ip igmp snooping fast-leave' % self._req_val)
|
||||
|
||||
def _generate_no_igmp_cmds(self):
|
||||
curr_state = self._current_config.get('state', 'enabled')
|
||||
if curr_state == 'enabled':
|
||||
self._commands.append('interface %s no ip igmp snooping fast-leave' % self._req_val)
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxIgmpInterfaceModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
435
plugins/modules/network/onyx/onyx_igmp_vlan.py
Normal file
435
plugins/modules/network/onyx/onyx_igmp_vlan.py
Normal file
@@ -0,0 +1,435 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_igmp_vlan
|
||||
author: Anas Badaha (@anasbadaha)
|
||||
short_description: Configures IGMP Vlan parameters
|
||||
description:
|
||||
- This module provides declarative management of IGMP vlan configuration on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.7.0932-01
|
||||
options:
|
||||
vlan_id:
|
||||
description:
|
||||
- VLAN ID, vlan should exist.
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- IGMP state.
|
||||
choices: ['enabled', 'disabled']
|
||||
default: enabled
|
||||
mrouter:
|
||||
description:
|
||||
- Configure ip igmp snooping mrouter port on vlan
|
||||
suboptions:
|
||||
state:
|
||||
description:
|
||||
- Enable IGMP snooping mrouter on vlan interface.
|
||||
choices: ['enabled', 'disabled']
|
||||
default: enabled
|
||||
name:
|
||||
description:
|
||||
- Configure mrouter interface
|
||||
required: true
|
||||
querier:
|
||||
description:
|
||||
- Configure the IGMP querier parameters
|
||||
suboptions:
|
||||
state:
|
||||
description:
|
||||
- Enable IGMP snooping querier on vlan in the switch.
|
||||
choices: ['enabled', 'disabled']
|
||||
default: enabled
|
||||
interval:
|
||||
description:
|
||||
- Update time interval between querier queries, range 60-600
|
||||
address:
|
||||
description:
|
||||
- Update IP address for the querier
|
||||
static_groups:
|
||||
description:
|
||||
- List of IGMP static groups.
|
||||
suboptions:
|
||||
multicast_ip_address:
|
||||
description:
|
||||
- Configure static IP multicast group, range 224.0.1.0-239.255.255.25.
|
||||
required: true
|
||||
name:
|
||||
description:
|
||||
- interface name to configure static groups on it.
|
||||
sources:
|
||||
description:
|
||||
- List of IP sources to be configured
|
||||
version:
|
||||
description:
|
||||
- IGMP snooping operation version on this vlan
|
||||
choices: ['V2','V3']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure igmp vlan
|
||||
onyx_igmp_vlan:
|
||||
state: enabled
|
||||
vlan_id: 10
|
||||
version:
|
||||
V2
|
||||
querier:
|
||||
state: enabled
|
||||
interval: 70
|
||||
address: 10.11.121.13
|
||||
mrouter:
|
||||
state: disabled
|
||||
name: Eth1/2
|
||||
static_groups:
|
||||
- multicast_ip_address: 224.5.5.8
|
||||
name: Eth1/1
|
||||
sources:
|
||||
- 1.1.1.1
|
||||
- 1.1.1.2
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- vlan 10 ip igmp snooping
|
||||
- vlan 10 ip igmp snooping static-group 224.5.5.5 interface ethernet 1/1
|
||||
"""
|
||||
import socket
|
||||
import struct
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
def _ip_to_int(addr):
|
||||
return struct.unpack("!I", socket.inet_aton(addr))[0]
|
||||
|
||||
|
||||
class OnyxIgmpVlanModule(BaseOnyxModule):
|
||||
MIN_MULTICAST_IP = _ip_to_int("224.0.1.0")
|
||||
MAX_MULTICAST_IP = _ip_to_int("239.255.255.255")
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
mrouter_spec = dict(name=dict(required=True),
|
||||
state=dict(choices=['enabled', 'disabled'], default='enabled'))
|
||||
querier_spec = dict(state=dict(choices=['enabled', 'disabled'], default='enabled'),
|
||||
interval=dict(type='int'), address=dict())
|
||||
static_groups_spec = dict(multicast_ip_address=dict(required=True),
|
||||
name=dict(required=True), sources=dict(type='list'))
|
||||
element_spec = dict(vlan_id=dict(type='int', required=True),
|
||||
state=dict(choices=['enabled', 'disabled'], default='enabled'),
|
||||
querier=dict(type='dict', options=querier_spec),
|
||||
static_groups=dict(type='list', elements='dict', options=static_groups_spec),
|
||||
mrouter=dict(type='dict', options=mrouter_spec),
|
||||
version=dict(choices=['V2', 'V3']))
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _validate_attr_is_not_none(self, attr_name, attr_value):
|
||||
if attr_name == 'vlan_id' or attr_name == 'state':
|
||||
pass
|
||||
elif attr_value is not None:
|
||||
self._module.fail_json(msg='Can not set %s value on switch while state is disabled' % attr_name)
|
||||
|
||||
def validate_param_values(self, obj, param=None):
|
||||
if obj['state'] == 'disabled':
|
||||
for attr_name in obj:
|
||||
self._validate_attr_is_not_none(attr_name, obj[attr_name])
|
||||
super(OnyxIgmpVlanModule, self).validate_param_values(obj, param)
|
||||
|
||||
def validate_querier(self, value):
|
||||
interval = value.get('interval')
|
||||
if interval and not 60 <= int(interval) <= 600:
|
||||
self._module.fail_json(msg='query-interval value must be between 60 and 600')
|
||||
|
||||
def validate_static_groups(self, value):
|
||||
multicast_ip = value.get('multicast_ip_address')
|
||||
multicast_ip = _ip_to_int(multicast_ip)
|
||||
|
||||
if multicast_ip < self.MIN_MULTICAST_IP or multicast_ip > self.MAX_MULTICAST_IP:
|
||||
self._module.fail_json(msg='multicast IP address must be in range 224.0.1.0 - 239.255.255.255')
|
||||
|
||||
@staticmethod
|
||||
def _get_curr_mrouter_config(mrouter_port):
|
||||
if mrouter_port == "none":
|
||||
return {'state': 'disabled'}
|
||||
else:
|
||||
return {'state': 'enabled',
|
||||
'name': mrouter_port}
|
||||
|
||||
def _get_curr_querier_config(self, querier_config):
|
||||
if "Non-Querier" in querier_config:
|
||||
return {'state': 'disabled'}
|
||||
elif "Querier" in querier_config:
|
||||
igmp_querier_config = self._show_igmp_querier_config()[0]
|
||||
snooping_querier_info = igmp_querier_config["Snooping querier information for VLAN %d" % (
|
||||
self._required_config['vlan_id'])]
|
||||
snooping_querier_info = snooping_querier_info[1]
|
||||
interval = int(snooping_querier_info["Query interval"])
|
||||
address = snooping_querier_info["Configured querier IP address"]
|
||||
return {'state': 'enabled',
|
||||
'interval': interval,
|
||||
'address': address}
|
||||
|
||||
@staticmethod
|
||||
def _get_curr_version(version):
|
||||
if "V3" in version:
|
||||
return "V3"
|
||||
elif "V2" in version:
|
||||
return "V2"
|
||||
|
||||
def _get_curr_static_group_config(self, multicast_ip_address):
|
||||
sources = None
|
||||
names = None
|
||||
igmp_snooping_groups_config = self._show_igmp_snooping_groups_config(multicast_ip_address)
|
||||
if igmp_snooping_groups_config is not None:
|
||||
igmp_snooping_groups_config = igmp_snooping_groups_config[0]
|
||||
snooping_group_information = igmp_snooping_groups_config.get('Snooping group '
|
||||
'information for VLAN %d and group '
|
||||
'%s' % (self._required_config['vlan_id'],
|
||||
multicast_ip_address))
|
||||
if snooping_group_information is not None:
|
||||
if len(snooping_group_information) == 1:
|
||||
names = snooping_group_information[0].get('V1/V2 Receiver Ports')
|
||||
elif len(snooping_group_information) == 2:
|
||||
sources_dict = dict()
|
||||
v3_receiver_ports = snooping_group_information[1].get('V3 Receiver Ports')
|
||||
ports_number = v3_receiver_ports[0].get('Port Number')
|
||||
sources = v3_receiver_ports[0].get('Include sources')
|
||||
if isinstance(ports_number, list):
|
||||
i = 0
|
||||
for port_number in ports_number:
|
||||
sources_dict[port_number] = sources[i]
|
||||
i += 1
|
||||
else:
|
||||
sources_dict[ports_number] = sources
|
||||
names = snooping_group_information[0].get('V1/V2 Receiver Ports')
|
||||
sources = sources_dict
|
||||
|
||||
return {'sources': sources,
|
||||
'names': names}
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
def _set_igmp_config(self, igmp_vlan_config):
|
||||
igmp_vlan_config = igmp_vlan_config[0]
|
||||
if not igmp_vlan_config:
|
||||
return
|
||||
|
||||
self._current_config['state'] = igmp_vlan_config.get('message 1')
|
||||
if "enabled" in self._current_config['state']:
|
||||
self._current_config['state'] = "enabled"
|
||||
elif "disabled" in self._current_config['state']:
|
||||
self._current_config['state'] = "disabled"
|
||||
|
||||
mrouter_port = igmp_vlan_config.get('mrouter static port list')
|
||||
self._current_config['mrouter'] = dict(self._get_curr_mrouter_config(mrouter_port))
|
||||
|
||||
querier_config = igmp_vlan_config.get('message 3')
|
||||
self._current_config['querier'] = dict(self._get_curr_querier_config(querier_config))
|
||||
|
||||
version = igmp_vlan_config.get('message 2')
|
||||
self._current_config['version'] = self._get_curr_version(version)
|
||||
|
||||
req_static_groups = self._required_config.get('static_groups')
|
||||
if req_static_groups is not None:
|
||||
static_groups = self._current_config['static_groups'] = dict()
|
||||
for static_group in req_static_groups:
|
||||
static_group_config = self._get_curr_static_group_config(static_group['multicast_ip_address'])
|
||||
static_groups[static_group['multicast_ip_address']] = static_group_config
|
||||
|
||||
def _show_igmp_vlan(self):
|
||||
cmd = ("show ip igmp snooping vlan %d" % self._required_config['vlan_id'])
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def _show_igmp_querier_config(self):
|
||||
cmd = ("show ip igmp snooping querier vlan %d " % self._required_config['vlan_id'])
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def _show_igmp_snooping_groups_config(self, multicast_ip_address):
|
||||
cmd = ("show ip igmp snooping groups vlan %d group %s" % (self._required_config['vlan_id'],
|
||||
multicast_ip_address))
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
igmp_vlan_config = self._show_igmp_vlan()
|
||||
if igmp_vlan_config:
|
||||
self._set_igmp_config(igmp_vlan_config)
|
||||
|
||||
def generate_commands(self):
|
||||
req_state = self._required_config.get('state', 'enabled')
|
||||
self._generate_igmp_vlan_cmds(req_state)
|
||||
|
||||
_mrouter = self._required_config.get('mrouter')
|
||||
if _mrouter is not None:
|
||||
self._generate_igmp_mrouter_cmds(_mrouter)
|
||||
|
||||
_querier = self._required_config.get('querier')
|
||||
if _querier is not None:
|
||||
req_querier_state = _querier.get('state', 'enabled')
|
||||
self._generate_igmp_querier_cmds(req_querier_state)
|
||||
|
||||
req_querier_interval = _querier.get('interval')
|
||||
if req_querier_interval is not None:
|
||||
self._gen_querier_attr_commands("interval", req_querier_interval, "query-interval")
|
||||
|
||||
req_querier_address = _querier.get('address')
|
||||
if req_querier_address is not None:
|
||||
self._gen_querier_attr_commands("address", req_querier_address, "address")
|
||||
|
||||
_version = self._required_config.get('version')
|
||||
if _version is not None:
|
||||
self._generate_igmp_version_cmds(_version)
|
||||
|
||||
_static_groups = self._required_config.get('static_groups')
|
||||
if _static_groups is not None:
|
||||
for static_group in _static_groups:
|
||||
self._generate_igmp_static_groups_cmd(static_group)
|
||||
|
||||
def _add_igmp_vlan_commands(self, req_state):
|
||||
if req_state == 'enabled':
|
||||
igmp_vlan_cmd = 'vlan %d ip igmp snooping' % self._required_config['vlan_id']
|
||||
else:
|
||||
igmp_vlan_cmd = 'vlan %d no ip igmp snooping' % self._required_config['vlan_id']
|
||||
|
||||
self._commands.append(igmp_vlan_cmd)
|
||||
|
||||
def _generate_igmp_vlan_cmds(self, req_state):
|
||||
curr_state = self._current_config.get('state')
|
||||
if curr_state != req_state:
|
||||
self._add_igmp_vlan_commands(req_state)
|
||||
|
||||
def _gen_querier_attr_commands(self, attr_name, req_attr_value, attr_cmd_name):
|
||||
_curr_querier = self._current_config.get('querier')
|
||||
curr_querier_val = _curr_querier.get(attr_name)
|
||||
if req_attr_value != curr_querier_val:
|
||||
self._commands.append('vlan %d ip igmp snooping querier %s %s' % (self._required_config['vlan_id'],
|
||||
attr_cmd_name, req_attr_value))
|
||||
|
||||
def _add_querier_commands(self, req_querier_state):
|
||||
if req_querier_state == 'enabled':
|
||||
self._commands.append('vlan %d ip igmp snooping querier' % self._required_config['vlan_id'])
|
||||
elif req_querier_state == 'disabled':
|
||||
self._commands.append('vlan %d no ip igmp snooping querier' % (
|
||||
self._required_config['vlan_id']))
|
||||
|
||||
def _generate_igmp_querier_cmds(self, req_querier_state):
|
||||
_curr_querier = self._current_config.get('querier')
|
||||
curr_querier_state = _curr_querier.get('state')
|
||||
if req_querier_state != curr_querier_state:
|
||||
self._add_querier_commands(req_querier_state)
|
||||
|
||||
def _generate_igmp_version_cmds(self, version):
|
||||
_curr_version = self._current_config.get('version')
|
||||
if version != _curr_version:
|
||||
self._commands.append('vlan %d ip igmp snooping version %s' % (
|
||||
self._required_config['vlan_id'], version[1]))
|
||||
|
||||
def _add_mrouter_commands(self, req_mrouter, curr_mrouter):
|
||||
curr_state = curr_mrouter.get('state')
|
||||
curr_interface = curr_mrouter.get('name')
|
||||
req_state = req_mrouter.get('state')
|
||||
req_interface = req_mrouter.get('name')
|
||||
mrouter_interface = req_interface.replace("Eth", "ethernet ")
|
||||
if curr_state == 'enabled' and req_state == 'disabled':
|
||||
self._commands.append('vlan %d no ip igmp snooping mrouter interface '
|
||||
'%s' % (self._required_config['vlan_id'], mrouter_interface))
|
||||
elif curr_state == 'disabled' and req_state == 'enabled':
|
||||
self._commands.append('vlan %d ip igmp snooping mrouter interface '
|
||||
'%s' % (self._required_config['vlan_id'], mrouter_interface))
|
||||
elif req_state == 'enabled' and curr_state == 'enabled' and req_interface != curr_interface:
|
||||
self._commands.append('vlan %d ip igmp snooping mrouter interface '
|
||||
'%s' % (self._required_config['vlan_id'], mrouter_interface))
|
||||
|
||||
def _generate_igmp_mrouter_cmds(self, req_mrouter):
|
||||
curr_mrouter = self._current_config.get('mrouter')
|
||||
if curr_mrouter != req_mrouter:
|
||||
self._add_mrouter_commands(req_mrouter, curr_mrouter)
|
||||
|
||||
def _add_igmp_static_groups_cmd(self, req_name, req_multicast_ip_address, curr_names):
|
||||
if curr_names is None:
|
||||
self._commands.append('vlan %d ip igmp snooping static-group %s interface %s' % (
|
||||
self._required_config['vlan_id'], req_multicast_ip_address, req_name.replace('Eth', 'ethernet ')))
|
||||
elif req_name.replace('E', 'e') not in curr_names:
|
||||
self._commands.append('vlan %d ip igmp snooping static-group %s interface %s' % (
|
||||
self._required_config['vlan_id'], req_multicast_ip_address, req_name.replace('Eth', 'ethernet ')))
|
||||
|
||||
def _add_igmp_static_groups_sources_cmd(self, req_sources, req_name, req_multicast_ip_address, curr_sources):
|
||||
if curr_sources is None:
|
||||
for source in req_sources:
|
||||
self._commands.append('vlan %d ip igmp snooping static-group %s interface %s source %s' % (
|
||||
self._required_config['vlan_id'], req_multicast_ip_address, req_name.replace('Eth', 'ethernet '),
|
||||
source))
|
||||
else:
|
||||
curr_sources = curr_sources.get(req_name.replace('E', 'e'))
|
||||
if curr_sources is None:
|
||||
curr_sources = set([])
|
||||
else:
|
||||
curr_sources = set(x.strip() for x in curr_sources.split(','))
|
||||
sources_to_add = set(req_sources) - set(curr_sources)
|
||||
sources_to_remove = set(curr_sources) - set(req_sources)
|
||||
if len(sources_to_add) != 0:
|
||||
for source in sources_to_add:
|
||||
self._commands.append('vlan %d ip igmp snooping static-group %s interface %s source %s' % (
|
||||
self._required_config['vlan_id'], req_multicast_ip_address,
|
||||
req_name.replace('Eth', 'ethernet '), source))
|
||||
if len(sources_to_remove) != 0:
|
||||
for source in sources_to_remove:
|
||||
self._commands.append('vlan %d no ip igmp snooping static-group %s interface %s source %s' % (
|
||||
self._required_config['vlan_id'], req_multicast_ip_address,
|
||||
req_name.replace('Eth', 'ethernet '),
|
||||
source))
|
||||
|
||||
def _generate_igmp_static_groups_cmd(self, static_group):
|
||||
req_multicast_ip_address = static_group.get('multicast_ip_address')
|
||||
req_name = static_group.get('name')
|
||||
req_sources = static_group.get('sources')
|
||||
curr_static_groups = self._current_config.get('static_groups')
|
||||
curr_static_group = curr_static_groups.get(req_multicast_ip_address)
|
||||
curr_names = None
|
||||
curr_sources = None
|
||||
if curr_static_group is not None:
|
||||
curr_names = curr_static_group.get('names')
|
||||
curr_sources = curr_static_group.get('sources')
|
||||
|
||||
self._add_igmp_static_groups_cmd(req_name, req_multicast_ip_address, curr_names)
|
||||
if req_sources is not None:
|
||||
self._add_igmp_static_groups_sources_cmd(req_sources, req_name, req_multicast_ip_address, curr_sources)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxIgmpVlanModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
501
plugins/modules/network/onyx/onyx_interface.py
Normal file
501
plugins/modules/network/onyx/onyx_interface.py
Normal file
@@ -0,0 +1,501 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_interface
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Manage Interfaces on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of Interfaces
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the Interface.
|
||||
required: true
|
||||
description:
|
||||
description:
|
||||
- Description of Interface.
|
||||
enabled:
|
||||
description:
|
||||
- Interface link status.
|
||||
type: bool
|
||||
speed:
|
||||
description:
|
||||
- Interface link speed.
|
||||
choices: ['1G', '10G', '25G', '40G', '50G', '56G', '100G']
|
||||
mtu:
|
||||
description:
|
||||
- Maximum size of transmit packet.
|
||||
aggregate:
|
||||
description: List of Interfaces definitions.
|
||||
duplex:
|
||||
description:
|
||||
- Interface link status
|
||||
default: auto
|
||||
choices: ['full', 'half', 'auto']
|
||||
tx_rate:
|
||||
description:
|
||||
- Transmit rate in bits per second (bps).
|
||||
- This is state check parameter only.
|
||||
- Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
|
||||
rx_rate:
|
||||
description:
|
||||
- Receiver rate in bits per second (bps).
|
||||
- This is state check parameter only.
|
||||
- Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
|
||||
delay:
|
||||
description:
|
||||
- Time in seconds to wait before checking for the operational state on
|
||||
remote device. This wait is applicable for operational state argument
|
||||
which are I(state) with values C(up)/C(down).
|
||||
default: 10
|
||||
purge:
|
||||
description:
|
||||
- Purge Interfaces not defined in the aggregate parameter.
|
||||
This applies only for logical interface.
|
||||
default: false
|
||||
type: bool
|
||||
state:
|
||||
description:
|
||||
- State of the Interface configuration, C(up) means present and
|
||||
operationally up and C(down) means present and operationally C(down)
|
||||
default: present
|
||||
choices: ['present', 'absent', 'up', 'down']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure interface
|
||||
onyx_interface:
|
||||
name: Eth1/2
|
||||
description: test-interface
|
||||
speed: 100G
|
||||
mtu: 512
|
||||
|
||||
- name: make interface up
|
||||
onyx_interface:
|
||||
name: Eth1/2
|
||||
enabled: True
|
||||
|
||||
- name: make interface down
|
||||
onyx_interface:
|
||||
name: Eth1/2
|
||||
enabled: False
|
||||
|
||||
- name: Check intent arguments
|
||||
onyx_interface:
|
||||
name: Eth1/2
|
||||
state: up
|
||||
|
||||
- name: Config + intent
|
||||
onyx_interface:
|
||||
name: Eth1/2
|
||||
enabled: False
|
||||
state: down
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- interface ethernet 1/2
|
||||
- description test-interface
|
||||
- mtu 512
|
||||
- exit
|
||||
"""
|
||||
|
||||
from copy import deepcopy
|
||||
import re
|
||||
from time import sleep
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import conditional
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_interfaces_config
|
||||
|
||||
|
||||
class OnyxInterfaceModule(BaseOnyxModule):
|
||||
IF_ETH_REGEX = re.compile(r"^Eth(\d+\/\d+|\d+\/\d+\/\d+)$")
|
||||
IF_VLAN_REGEX = re.compile(r"^Vlan (\d+)$")
|
||||
IF_LOOPBACK_REGEX = re.compile(r"^Loopback (\d+)$")
|
||||
IF_PO_REGEX = re.compile(r"^Po(\d+)$")
|
||||
|
||||
IF_TYPE_ETH = "ethernet"
|
||||
IF_TYPE_LOOPBACK = "loopback"
|
||||
IF_TYPE_VLAN = "vlan"
|
||||
IF_TYPE_PO = "port-channel"
|
||||
|
||||
IF_TYPE_MAP = {
|
||||
IF_TYPE_ETH: IF_ETH_REGEX,
|
||||
IF_TYPE_VLAN: IF_VLAN_REGEX,
|
||||
IF_TYPE_LOOPBACK: IF_LOOPBACK_REGEX,
|
||||
IF_TYPE_PO: IF_PO_REGEX
|
||||
}
|
||||
UNSUPPORTED_ATTRS = {
|
||||
IF_TYPE_ETH: (),
|
||||
IF_TYPE_VLAN: ('speed', 'rx_rate', 'tx_rate'),
|
||||
IF_TYPE_LOOPBACK: ('speed', 'mtu', 'rx_rate', 'tx_rate'),
|
||||
IF_TYPE_PO: ('speed', 'rx_rate', 'tx_rate'),
|
||||
}
|
||||
UNSUPPORTED_STATES = {
|
||||
IF_TYPE_ETH: ('absent',),
|
||||
IF_TYPE_VLAN: (),
|
||||
IF_TYPE_LOOPBACK: ('up', 'down'),
|
||||
IF_TYPE_PO: ('absent'),
|
||||
}
|
||||
|
||||
IF_MODIFIABLE_ATTRS = ('speed', 'description', 'mtu')
|
||||
_interface_type = None
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
return dict(
|
||||
name=dict(type='str'),
|
||||
description=dict(),
|
||||
speed=dict(choices=['1G', '10G', '25G', '40G', '50G', '56G', '100G']),
|
||||
mtu=dict(type='int'),
|
||||
enabled=dict(type='bool'),
|
||||
delay=dict(default=10, type='int'),
|
||||
state=dict(default='present',
|
||||
choices=['present', 'absent', 'up', 'down']),
|
||||
tx_rate=dict(),
|
||||
rx_rate=dict(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_aggregate_spec(cls, element_spec):
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
return aggregate_spec
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
aggregate_spec = self._get_aggregate_spec(element_spec)
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict',
|
||||
options=aggregate_spec),
|
||||
purge=dict(default=False, type='bool'),
|
||||
)
|
||||
argument_spec.update(element_spec)
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate']]
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
def validate_purge(self, value):
|
||||
if value:
|
||||
self._module.fail_json(
|
||||
msg='Purge is not supported!')
|
||||
|
||||
def validate_duplex(self, value):
|
||||
if value != 'auto':
|
||||
self._module.fail_json(
|
||||
msg='Duplex is not supported!')
|
||||
|
||||
def _get_interface_type(self, if_name):
|
||||
if_type = None
|
||||
if_id = None
|
||||
for interface_type, interface_regex in iteritems(self.IF_TYPE_MAP):
|
||||
match = interface_regex.match(if_name)
|
||||
if match:
|
||||
if_type = interface_type
|
||||
if_id = match.group(1)
|
||||
break
|
||||
return if_type, if_id
|
||||
|
||||
def _set_if_type(self, params):
|
||||
if_name = params['name']
|
||||
if_type, if_id = self._get_interface_type(if_name)
|
||||
if not if_id:
|
||||
self._module.fail_json(
|
||||
msg='unsupported interface: %s' % if_name)
|
||||
params['if_type'] = if_type
|
||||
params['if_id'] = if_id
|
||||
|
||||
def _check_supported_attrs(self, if_obj):
|
||||
unsupported_attrs = self.UNSUPPORTED_ATTRS[self._interface_type]
|
||||
for attr in unsupported_attrs:
|
||||
val = if_obj[attr]
|
||||
if val is not None:
|
||||
self._module.fail_json(
|
||||
msg='attribute %s is not supported for %s interface' % (
|
||||
attr, self._interface_type))
|
||||
req_state = if_obj['state']
|
||||
unsupported_states = self.UNSUPPORTED_STATES[self._interface_type]
|
||||
if req_state in unsupported_states:
|
||||
self._module.fail_json(
|
||||
msg='%s state is not supported for %s interface' % (
|
||||
req_state, self._interface_type))
|
||||
|
||||
def _validate_interface_type(self):
|
||||
for if_obj in self._required_config:
|
||||
if_type = if_obj['if_type']
|
||||
if not self._interface_type:
|
||||
self._interface_type = if_type
|
||||
elif self._interface_type != if_type:
|
||||
self._module.fail_json(
|
||||
msg='Cannot aggregate interfaces from different types')
|
||||
self._check_supported_attrs(if_obj)
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = list()
|
||||
module_params = self._module.params
|
||||
aggregate = module_params.get('aggregate')
|
||||
if aggregate:
|
||||
for item in aggregate:
|
||||
for key in item:
|
||||
if item.get(key) is None:
|
||||
item[key] = module_params[key]
|
||||
|
||||
self.validate_param_values(item, item)
|
||||
req_item = item.copy()
|
||||
self._set_if_type(req_item)
|
||||
self._required_config.append(req_item)
|
||||
else:
|
||||
params = {
|
||||
'name': module_params['name'],
|
||||
'description': module_params['description'],
|
||||
'speed': module_params['speed'],
|
||||
'mtu': module_params['mtu'],
|
||||
'state': module_params['state'],
|
||||
'delay': module_params['delay'],
|
||||
'enabled': module_params['enabled'],
|
||||
'tx_rate': module_params['tx_rate'],
|
||||
'rx_rate': module_params['rx_rate'],
|
||||
}
|
||||
|
||||
self.validate_param_values(params)
|
||||
self._set_if_type(params)
|
||||
self._required_config.append(params)
|
||||
self._validate_interface_type()
|
||||
|
||||
@classmethod
|
||||
def get_if_name(cls, item):
|
||||
return cls.get_config_attr(item, "header")
|
||||
|
||||
@classmethod
|
||||
def get_admin_state(cls, item):
|
||||
admin_state = cls.get_config_attr(item, "Admin state")
|
||||
return str(admin_state).lower() == "enabled"
|
||||
|
||||
@classmethod
|
||||
def get_oper_state(cls, item):
|
||||
oper_state = cls.get_config_attr(item, "Operational state")
|
||||
if not oper_state:
|
||||
oper_state = cls.get_config_attr(item, "State")
|
||||
return str(oper_state).lower()
|
||||
|
||||
@classmethod
|
||||
def get_speed(cls, item):
|
||||
speed = cls.get_config_attr(item, 'Actual speed')
|
||||
if not speed:
|
||||
return
|
||||
try:
|
||||
speed = int(speed.split()[0])
|
||||
return "%dG" % speed
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def _create_if_data(self, name, item):
|
||||
regex = self.IF_TYPE_MAP[self._interface_type]
|
||||
if_id = ''
|
||||
match = regex.match(name)
|
||||
if match:
|
||||
if_id = match.group(1)
|
||||
return dict(
|
||||
name=name,
|
||||
description=self.get_config_attr(item, 'Description'),
|
||||
speed=self.get_speed(item),
|
||||
mtu=self.get_mtu(item),
|
||||
enabled=self.get_admin_state(item),
|
||||
state=self.get_oper_state(item),
|
||||
if_id=if_id)
|
||||
|
||||
def _get_interfaces_config(self):
|
||||
return get_interfaces_config(self._module, self._interface_type)
|
||||
|
||||
def load_current_config(self):
|
||||
self._os_version = self._get_os_version()
|
||||
self._current_config = dict()
|
||||
config = self._get_interfaces_config()
|
||||
if not config:
|
||||
return
|
||||
if self._os_version < self.ONYX_API_VERSION:
|
||||
for if_data in config:
|
||||
if_name = self.get_if_name(if_data)
|
||||
self._current_config[if_name] = self._create_if_data(
|
||||
if_name, if_data)
|
||||
else:
|
||||
if_data = dict()
|
||||
for if_config in config:
|
||||
for if_name, if_attr in iteritems(if_config):
|
||||
for config in if_attr:
|
||||
for key, value in iteritems(config):
|
||||
if_data[key] = value
|
||||
self._current_config[if_name] = self._create_if_data(
|
||||
if_name, if_data)
|
||||
|
||||
def _generate_no_if_commands(self, req_if, curr_if):
|
||||
if self._interface_type == self.IF_TYPE_ETH:
|
||||
name = req_if['name']
|
||||
self._module.fail_json(
|
||||
msg='cannot remove ethernet interface %s' % name)
|
||||
if not curr_if:
|
||||
return
|
||||
if_id = req_if['if_id']
|
||||
if not if_id:
|
||||
return
|
||||
self._commands.append(
|
||||
'no interface %s %s' % (self._interface_type, if_id))
|
||||
|
||||
def _add_commands_to_interface(self, req_if, cmd_list):
|
||||
if not cmd_list:
|
||||
return
|
||||
if_id = req_if['if_id']
|
||||
if not if_id:
|
||||
return
|
||||
self._commands.append(
|
||||
'interface %s %s' % (self._interface_type, if_id))
|
||||
self._commands.extend(cmd_list)
|
||||
self._commands.append('exit')
|
||||
|
||||
def _generate_if_commands(self, req_if, curr_if):
|
||||
enabled = req_if['enabled']
|
||||
cmd_list = []
|
||||
for attr_name in self.IF_MODIFIABLE_ATTRS:
|
||||
candidate = req_if.get(attr_name)
|
||||
running = curr_if.get(attr_name)
|
||||
if candidate != running:
|
||||
if candidate:
|
||||
cmd = attr_name + ' ' + str(candidate)
|
||||
if self._interface_type == self.IF_TYPE_ETH and \
|
||||
attr_name in ('mtu', 'speed'):
|
||||
cmd = cmd + ' ' + 'force'
|
||||
cmd_list.append(cmd)
|
||||
curr_enabled = curr_if.get('enabled', False)
|
||||
if enabled is not None and enabled != curr_enabled:
|
||||
cmd = 'shutdown'
|
||||
if enabled:
|
||||
cmd = "no %s" % cmd
|
||||
cmd_list.append(cmd)
|
||||
if cmd_list:
|
||||
self._add_commands_to_interface(req_if, cmd_list)
|
||||
|
||||
def generate_commands(self):
|
||||
for req_if in self._required_config:
|
||||
name = req_if['name']
|
||||
curr_if = self._current_config.get(name, {})
|
||||
if not curr_if and self._interface_type == self.IF_TYPE_ETH:
|
||||
self._module.fail_json(
|
||||
msg='could not find ethernet interface %s' % name)
|
||||
continue
|
||||
req_state = req_if['state']
|
||||
if req_state == 'absent':
|
||||
self._generate_no_if_commands(req_if, curr_if)
|
||||
else:
|
||||
self._generate_if_commands(req_if, curr_if)
|
||||
|
||||
def _get_interfaces_rates(self):
|
||||
return get_interfaces_config(self._module, self._interface_type,
|
||||
"rates")
|
||||
|
||||
def _get_interfaces_status(self):
|
||||
return get_interfaces_config(self._module, self._interface_type,
|
||||
"status")
|
||||
|
||||
def _check_state(self, name, want_state, statuses):
|
||||
curr_if = statuses.get(name, {})
|
||||
if curr_if:
|
||||
curr_if = curr_if[0]
|
||||
curr_state = self.get_oper_state(curr_if).strip()
|
||||
if curr_state is None or not conditional(want_state, curr_state):
|
||||
return 'state eq(%s)' % want_state
|
||||
|
||||
def check_declarative_intent_params(self, result):
|
||||
failed_conditions = []
|
||||
delay_called = False
|
||||
rates = None
|
||||
statuses = None
|
||||
for req_if in self._required_config:
|
||||
want_state = req_if.get('state')
|
||||
want_tx_rate = req_if.get('tx_rate')
|
||||
want_rx_rate = req_if.get('rx_rate')
|
||||
name = req_if['name']
|
||||
if want_state not in ('up', 'down') and not want_tx_rate and not \
|
||||
want_rx_rate:
|
||||
continue
|
||||
if not delay_called and result['changed']:
|
||||
delay_called = True
|
||||
delay = req_if['delay']
|
||||
if delay > 0:
|
||||
sleep(delay)
|
||||
if want_state in ('up', 'down'):
|
||||
if statuses is None:
|
||||
statuses = self._get_interfaces_status() or {}
|
||||
cond = self._check_state(name, want_state, statuses)
|
||||
if cond:
|
||||
failed_conditions.append(cond)
|
||||
if_rates = None
|
||||
if want_tx_rate or want_rx_rate:
|
||||
if not rates:
|
||||
rates = self._get_interfaces_rates()
|
||||
if_rates = rates.get(name)
|
||||
if if_rates:
|
||||
if_rates = if_rates[0]
|
||||
if want_tx_rate:
|
||||
have_tx_rate = None
|
||||
if if_rates:
|
||||
have_tx_rate = if_rates.get('egress rate')
|
||||
if have_tx_rate:
|
||||
have_tx_rate = have_tx_rate.split()[0]
|
||||
if have_tx_rate is None or not \
|
||||
conditional(want_tx_rate, have_tx_rate.strip(),
|
||||
cast=int):
|
||||
failed_conditions.append('tx_rate ' + want_tx_rate)
|
||||
|
||||
if want_rx_rate:
|
||||
have_rx_rate = None
|
||||
if if_rates:
|
||||
have_rx_rate = if_rates.get('ingress rate')
|
||||
if have_rx_rate:
|
||||
have_rx_rate = have_rx_rate.split()[0]
|
||||
if have_rx_rate is None or not \
|
||||
conditional(want_rx_rate, have_rx_rate.strip(),
|
||||
cast=int):
|
||||
failed_conditions.append('rx_rate ' + want_rx_rate)
|
||||
|
||||
return failed_conditions
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxInterfaceModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
298
plugins/modules/network/onyx/onyx_l2_interface.py
Normal file
298
plugins/modules/network/onyx/onyx_l2_interface.py
Normal file
@@ -0,0 +1,298 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_l2_interface
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Manage Layer-2 interface on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of Layer-2 interface
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the interface.
|
||||
aggregate:
|
||||
description:
|
||||
- List of Layer-2 interface definitions.
|
||||
mode:
|
||||
description:
|
||||
- Mode in which interface needs to be configured.
|
||||
default: access
|
||||
choices: ['access', 'trunk', 'hybrid']
|
||||
access_vlan:
|
||||
description:
|
||||
- Configure given VLAN in access port.
|
||||
trunk_allowed_vlans:
|
||||
description:
|
||||
- List of allowed VLANs in a given trunk port.
|
||||
state:
|
||||
description:
|
||||
- State of the Layer-2 Interface configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure Layer-2 interface
|
||||
onyx_l2_interface:
|
||||
name: Eth1/1
|
||||
mode: access
|
||||
access_vlan: 30
|
||||
- name: remove Layer-2 interface configuration
|
||||
onyx_l2_interface:
|
||||
name: Eth1/1
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device
|
||||
returned: always.
|
||||
type: list
|
||||
sample:
|
||||
- interface ethernet 1/1
|
||||
- switchport mode access
|
||||
- switchport access vlan 30
|
||||
"""
|
||||
from copy import deepcopy
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_interfaces_config
|
||||
|
||||
|
||||
class OnyxL2InterfaceModule(BaseOnyxModule):
|
||||
IFNAME_REGEX = re.compile(r"^.*(Eth\d+\/\d+|Mpo\d+|Po\d+)")
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
return dict(
|
||||
name=dict(),
|
||||
access_vlan=dict(type='int'),
|
||||
trunk_allowed_vlans=dict(type='list', elements='int'),
|
||||
state=dict(default='present',
|
||||
choices=['present', 'absent']),
|
||||
mode=dict(default='access',
|
||||
choices=['access', 'hybrid', 'trunk']),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_aggregate_spec(cls, element_spec):
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
return aggregate_spec
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
aggregate_spec = self._get_aggregate_spec(element_spec)
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict',
|
||||
options=aggregate_spec),
|
||||
)
|
||||
argument_spec.update(element_spec)
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate']]
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = list()
|
||||
module_params = self._module.params
|
||||
aggregate = module_params.get('aggregate')
|
||||
if aggregate:
|
||||
for item in aggregate:
|
||||
for key in item:
|
||||
if item.get(key) is None:
|
||||
item[key] = module_params[key]
|
||||
self.validate_param_values(item, item)
|
||||
req_item = item.copy()
|
||||
self._required_config.append(req_item)
|
||||
else:
|
||||
params = {
|
||||
'name': module_params['name'],
|
||||
'access_vlan': module_params['access_vlan'],
|
||||
'trunk_allowed_vlans': module_params['trunk_allowed_vlans'],
|
||||
'mode': module_params['mode'],
|
||||
'state': module_params['state'],
|
||||
}
|
||||
self.validate_param_values(params)
|
||||
self._required_config.append(params)
|
||||
|
||||
def validate_access_vlan(self, value):
|
||||
if value and not 1 <= int(value) <= 4094:
|
||||
self._module.fail_json(msg='vlan id must be between 1 and 4094')
|
||||
|
||||
@classmethod
|
||||
def get_allowed_vlans(cls, if_data):
|
||||
allowed_vlans = cls.get_config_attr(if_data, 'Allowed vlans')
|
||||
interface_allwoed_vlans = []
|
||||
if allowed_vlans:
|
||||
vlans = [x.strip() for x in allowed_vlans.split(',')]
|
||||
for vlan in vlans:
|
||||
if '-' not in vlan:
|
||||
interface_allwoed_vlans.append(int(vlan))
|
||||
else:
|
||||
vlan_range = vlan.split("-")
|
||||
min_number = int(vlan_range[0].strip())
|
||||
max_number = int(vlan_range[1].strip())
|
||||
vlan_list = range(min_number, max_number + 1)
|
||||
interface_allwoed_vlans.extend(vlan_list)
|
||||
return interface_allwoed_vlans
|
||||
|
||||
@classmethod
|
||||
def get_access_vlan(cls, if_data):
|
||||
access_vlan = cls.get_config_attr(if_data, 'Access vlan')
|
||||
if access_vlan:
|
||||
try:
|
||||
return int(access_vlan)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def _create_switchport_data(self, if_name, if_data):
|
||||
if self._os_version >= self.ONYX_API_VERSION:
|
||||
if_data = if_data[0]
|
||||
|
||||
return {
|
||||
'name': if_name,
|
||||
'mode': self.get_config_attr(if_data, 'Mode'),
|
||||
'access_vlan': self.get_access_vlan(if_data),
|
||||
'trunk_allowed_vlans': self.get_allowed_vlans(if_data)
|
||||
}
|
||||
|
||||
def _get_switchport_config(self):
|
||||
return get_interfaces_config(self._module, 'switchport')
|
||||
|
||||
def load_current_config(self):
|
||||
# called in base class in run function
|
||||
self._os_version = self._get_os_version()
|
||||
self._current_config = dict()
|
||||
switchports_config = self._get_switchport_config()
|
||||
if not switchports_config:
|
||||
return
|
||||
for if_name, if_data in iteritems(switchports_config):
|
||||
self._current_config[if_name] = \
|
||||
self._create_switchport_data(if_name, if_data)
|
||||
|
||||
def _get_switchport_command_name(self, if_name):
|
||||
if if_name.startswith('Eth'):
|
||||
return if_name.replace("Eth", "ethernet ")
|
||||
if if_name.startswith('Po'):
|
||||
return if_name.replace("Po", "port-channel ")
|
||||
if if_name.startswith('Mpo'):
|
||||
return if_name.replace("Mpo", "mlag-port-channel ")
|
||||
self._module.fail_json(
|
||||
msg='invalid interface name: %s' % if_name)
|
||||
|
||||
def _add_interface_commands(self, if_name, commands):
|
||||
if_cmd_name = self._get_switchport_command_name(if_name)
|
||||
self._commands.append("interface %s" % if_cmd_name)
|
||||
self._commands.extend(commands)
|
||||
self._commands.append('exit')
|
||||
|
||||
def _generate_no_switchport_commands(self, if_name):
|
||||
commands = ['no switchport force']
|
||||
self._add_interface_commands(if_name, commands)
|
||||
|
||||
def _generate_switchport_commands(self, if_name, req_conf):
|
||||
commands = []
|
||||
curr_conf = self._current_config.get(if_name, {})
|
||||
curr_mode = curr_conf.get('mode')
|
||||
req_mode = req_conf.get('mode')
|
||||
if req_mode != curr_mode:
|
||||
commands.append('switchport mode %s' % req_mode)
|
||||
curr_access_vlan = curr_conf.get('access_vlan')
|
||||
req_access_vlan = req_conf.get('access_vlan')
|
||||
if curr_access_vlan != req_access_vlan and req_access_vlan:
|
||||
commands.append('switchport access vlan %s' % req_access_vlan)
|
||||
curr_trunk_vlans = curr_conf.get('trunk_allowed_vlans') or set()
|
||||
if curr_trunk_vlans:
|
||||
curr_trunk_vlans = set(curr_trunk_vlans)
|
||||
req_trunk_vlans = req_conf.get('trunk_allowed_vlans') or set()
|
||||
if req_trunk_vlans:
|
||||
req_trunk_vlans = set(req_trunk_vlans)
|
||||
if req_mode != 'access' and curr_trunk_vlans != req_trunk_vlans:
|
||||
added_vlans = req_trunk_vlans - curr_trunk_vlans
|
||||
for vlan_id in added_vlans:
|
||||
commands.append('switchport %s allowed-vlan add %s' %
|
||||
(req_mode, vlan_id))
|
||||
removed_vlans = curr_trunk_vlans - req_trunk_vlans
|
||||
for vlan_id in removed_vlans:
|
||||
commands.append('switchport %s allowed-vlan remove %s' %
|
||||
(req_mode, vlan_id))
|
||||
|
||||
if commands:
|
||||
self._add_interface_commands(if_name, commands)
|
||||
|
||||
def generate_commands(self):
|
||||
for req_conf in self._required_config:
|
||||
state = req_conf['state']
|
||||
if_name = req_conf['name']
|
||||
if state == 'absent':
|
||||
if if_name in self._current_config:
|
||||
self._generate_no_switchport_commands(if_name)
|
||||
else:
|
||||
self._generate_switchport_commands(if_name, req_conf)
|
||||
|
||||
def _generate_vlan_commands(self, vlan_id, req_conf):
|
||||
curr_vlan = self._current_config.get(vlan_id, {})
|
||||
if not curr_vlan:
|
||||
cmd = "vlan " + vlan_id
|
||||
self._commands.append("vlan %s" % vlan_id)
|
||||
self._commands.append("exit")
|
||||
vlan_name = req_conf['vlan_name']
|
||||
if vlan_name:
|
||||
if vlan_name != curr_vlan.get('vlan_name'):
|
||||
self._commands.append("vlan %s name %s" % (vlan_id, vlan_name))
|
||||
curr_members = set(curr_vlan.get('interfaces', []))
|
||||
req_members = req_conf['interfaces']
|
||||
mode = req_conf['mode']
|
||||
for member in req_members:
|
||||
if member in curr_members:
|
||||
continue
|
||||
if_name = self.get_switchport_command_name(member)
|
||||
cmd = "interface %s switchport mode %s" % (if_name, mode)
|
||||
self._commands.append(cmd)
|
||||
cmd = "interface %s switchport %s allowed-vlan add %s" % (
|
||||
if_name, mode, vlan_id)
|
||||
self._commands.append(cmd)
|
||||
req_members = set(req_members)
|
||||
for member in curr_members:
|
||||
if member in req_members:
|
||||
continue
|
||||
if_name = self.get_switchport_command_name(member)
|
||||
cmd = "interface %s switchport %s allowed-vlan remove %s" % (
|
||||
if_name, mode, vlan_id)
|
||||
self._commands.append(cmd)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxL2InterfaceModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
301
plugins/modules/network/onyx/onyx_l3_interface.py
Normal file
301
plugins/modules/network/onyx/onyx_l3_interface.py
Normal file
@@ -0,0 +1,301 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_l3_interface
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Manage L3 interfaces on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of L3 interfaces
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the L3 interface.
|
||||
ipv4:
|
||||
description:
|
||||
- IPv4 of the L3 interface.
|
||||
ipv6:
|
||||
description:
|
||||
- IPv6 of the L3 interface (not supported for now).
|
||||
aggregate:
|
||||
description: List of L3 interfaces definitions
|
||||
purge:
|
||||
description:
|
||||
- Purge L3 interfaces not defined in the I(aggregate) parameter.
|
||||
default: false
|
||||
type: bool
|
||||
state:
|
||||
description:
|
||||
- State of the L3 interface configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Set Eth1/1 IPv4 address
|
||||
onyx_l3_interface:
|
||||
name: Eth1/1
|
||||
ipv4: 192.168.0.1/24
|
||||
|
||||
- name: Remove Eth1/1 IPv4 address
|
||||
onyx_l3_interface:
|
||||
name: Eth1/1
|
||||
state: absent
|
||||
|
||||
- name: Set IP addresses on aggregate
|
||||
onyx_l3_interface:
|
||||
aggregate:
|
||||
- { name: Eth1/1, ipv4: 192.168.2.10/24 }
|
||||
- { name: Eth1/2, ipv4: 192.168.3.10/24 }
|
||||
|
||||
- name: Remove IP addresses on aggregate
|
||||
onyx_l3_interface:
|
||||
aggregate:
|
||||
- { name: Eth1/1, ipv4: 192.168.2.10/24 }
|
||||
- { name: Eth1/2, ipv4: 192.168.3.10/24 }
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device
|
||||
returned: always.
|
||||
type: list
|
||||
sample:
|
||||
- interfaces ethernet 1/1 ip address 192.168.0.1 /24
|
||||
"""
|
||||
import re
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_interfaces_config
|
||||
|
||||
|
||||
class OnyxL3InterfaceModule(BaseOnyxModule):
|
||||
IF_ETH_REGEX = re.compile(r"^Eth(\d+\/\d+|Eth\d+\/\d+\d+)$")
|
||||
IF_VLAN_REGEX = re.compile(r"^Vlan (\d+)$")
|
||||
IF_LOOPBACK_REGEX = re.compile(r"^Loopback (\d+)$")
|
||||
|
||||
IF_TYPE_ETH = "ethernet"
|
||||
IF_TYPE_LOOPBACK = "loopback"
|
||||
IF_TYPE_VLAN = "vlan"
|
||||
|
||||
IF_TYPE_MAP = {
|
||||
IF_TYPE_ETH: IF_ETH_REGEX,
|
||||
IF_TYPE_VLAN: IF_VLAN_REGEX,
|
||||
IF_TYPE_LOOPBACK: IF_LOOPBACK_REGEX,
|
||||
}
|
||||
|
||||
IP_ADDR_ATTR_MAP = {
|
||||
IF_TYPE_ETH: 'IP Address',
|
||||
IF_TYPE_VLAN: 'Internet Address',
|
||||
IF_TYPE_LOOPBACK: 'Internet Address',
|
||||
}
|
||||
|
||||
_purge = False
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
return dict(
|
||||
name=dict(type='str'),
|
||||
ipv4=dict(type='str'),
|
||||
ipv6=dict(type='str'),
|
||||
state=dict(default='present',
|
||||
choices=['present', 'absent', 'enabled', 'disabled']),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_aggregate_spec(cls, element_spec):
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
return aggregate_spec
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
aggregate_spec = self._get_aggregate_spec(element_spec)
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict',
|
||||
options=aggregate_spec),
|
||||
purge=dict(default=False, type='bool'),
|
||||
)
|
||||
argument_spec.update(element_spec)
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate']]
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
def _get_interface_type(self, if_name):
|
||||
if_type = None
|
||||
if_id = None
|
||||
for interface_type, interface_regex in iteritems(self.IF_TYPE_MAP):
|
||||
match = interface_regex.match(if_name)
|
||||
if match:
|
||||
if_type = interface_type
|
||||
if_id = match.group(1)
|
||||
break
|
||||
return if_type, if_id
|
||||
|
||||
def _set_if_type(self, params):
|
||||
if_name = params['name']
|
||||
if_type, if_id = self._get_interface_type(if_name)
|
||||
if not if_id:
|
||||
self._module.fail_json(
|
||||
msg='unsupported interface: %s' % if_name)
|
||||
params['if_type'] = if_type
|
||||
params['if_id'] = if_id
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = list()
|
||||
module_params = self._module.params
|
||||
aggregate = module_params.get('aggregate')
|
||||
self._purge = module_params.get('purge', False)
|
||||
if aggregate:
|
||||
for item in aggregate:
|
||||
for key in item:
|
||||
if item.get(key) is None:
|
||||
item[key] = module_params[key]
|
||||
self.validate_param_values(item, item)
|
||||
req_item = item.copy()
|
||||
self._set_if_type(req_item)
|
||||
self._required_config.append(req_item)
|
||||
else:
|
||||
params = {
|
||||
'name': module_params['name'],
|
||||
'ipv4': module_params['ipv4'],
|
||||
'ipv6': module_params['ipv6'],
|
||||
'state': module_params['state'],
|
||||
}
|
||||
self.validate_param_values(params)
|
||||
self._set_if_type(params)
|
||||
self._required_config.append(params)
|
||||
|
||||
def _get_interfaces_config(self, interface_type):
|
||||
return get_interfaces_config(self._module, interface_type)
|
||||
|
||||
def _parse_interfaces_config(self, if_type, if_config):
|
||||
if self._os_version < self.ONYX_API_VERSION:
|
||||
for if_data in if_config:
|
||||
if_name = self.get_config_attr(if_data, 'header')
|
||||
self._get_if_attributes(if_type, if_name, if_data)
|
||||
else:
|
||||
for if_config_item in if_config:
|
||||
for if_name, if_data in iteritems(if_config_item):
|
||||
if_data = if_data[0]
|
||||
self._get_if_attributes(if_type, if_name, if_data)
|
||||
|
||||
def _get_if_attributes(self, if_type, if_name, if_data):
|
||||
ipaddr_attr = self.IP_ADDR_ATTR_MAP[if_type]
|
||||
regex = self.IF_TYPE_MAP[if_type]
|
||||
match = regex.match(if_name)
|
||||
if not match:
|
||||
return
|
||||
ipv4 = self.get_config_attr(if_data, ipaddr_attr)
|
||||
if ipv4:
|
||||
ipv4 = ipv4.replace(' ', '')
|
||||
ipv6 = self.get_config_attr(if_data, 'IPv6 address(es)')
|
||||
if ipv6:
|
||||
ipv6 = ipv6.replace('[primary]', '')
|
||||
ipv6 = ipv6.strip()
|
||||
if_id = match.group(1)
|
||||
switchport = self.get_config_attr(if_data, 'Switchport mode')
|
||||
if_obj = {
|
||||
'name': if_name,
|
||||
'if_id': if_id,
|
||||
'if_type': if_type,
|
||||
'ipv4': ipv4,
|
||||
'ipv6': ipv6,
|
||||
'switchport': switchport,
|
||||
}
|
||||
self._current_config[if_name] = if_obj
|
||||
|
||||
def load_current_config(self):
|
||||
# called in base class in run function
|
||||
self._os_version = self._get_os_version()
|
||||
self._current_config = dict()
|
||||
if_types = set([if_obj['if_type'] for if_obj in self._required_config])
|
||||
for if_type in if_types:
|
||||
if_config = self._get_interfaces_config(if_type)
|
||||
if not if_config:
|
||||
continue
|
||||
self._parse_interfaces_config(if_type, if_config)
|
||||
|
||||
def _generate_no_ip_commands(self, req_conf, curr_conf):
|
||||
curr_ip = curr_conf.get('ipv4')
|
||||
if_type = req_conf['if_type']
|
||||
if_id = req_conf['if_id']
|
||||
if curr_ip:
|
||||
cmd = "interface %s %s no ip address" % (if_type, if_id)
|
||||
self._commands.append(cmd)
|
||||
curr_ipv6 = curr_conf.get('ipv6')
|
||||
if curr_ipv6:
|
||||
cmd = "interface %s %s no ipv6 address %s" % (
|
||||
if_type, if_id, curr_ipv6)
|
||||
self._commands.append(cmd)
|
||||
|
||||
def _generate_ip_commands(self, req_conf, curr_conf):
|
||||
curr_ipv4 = curr_conf.get('ipv4')
|
||||
req_ipv4 = req_conf.get('ipv4')
|
||||
curr_ipv6 = curr_conf.get('ipv6')
|
||||
req_ipv6 = req_conf.get('ipv6')
|
||||
if_type = req_conf['if_type']
|
||||
if_id = req_conf['if_id']
|
||||
switchport = curr_conf.get('switchport')
|
||||
if switchport:
|
||||
cmd = "interface %s %s no switchport force" % (if_type, if_id)
|
||||
self._commands.append(cmd)
|
||||
if curr_ipv4 != req_ipv4:
|
||||
cmd = "interface %s %s ip address %s" % (if_type, if_id, req_ipv4)
|
||||
self._commands.append(cmd)
|
||||
if curr_ipv6 != req_ipv6:
|
||||
cmd = "interface %s %s ipv6 address %s" % (
|
||||
if_type, if_id, req_ipv6)
|
||||
self._commands.append(cmd)
|
||||
|
||||
def generate_commands(self):
|
||||
req_interfaces = set()
|
||||
for req_conf in self._required_config:
|
||||
state = req_conf['state']
|
||||
if_name = req_conf['name']
|
||||
curr_conf = self._current_config.get(if_name, {})
|
||||
if state == 'absent':
|
||||
self._generate_no_ip_commands(req_conf, curr_conf)
|
||||
else:
|
||||
req_interfaces.add(if_name)
|
||||
self._generate_ip_commands(req_conf, curr_conf)
|
||||
if self._purge:
|
||||
for if_name, curr_conf in iteritems(self._current_config):
|
||||
if if_name not in req_interfaces:
|
||||
self._generate_no_ip_commands(req_conf, curr_conf)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxL3InterfaceModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
353
plugins/modules/network/onyx/onyx_linkagg.py
Normal file
353
plugins/modules/network/onyx/onyx_linkagg.py
Normal file
@@ -0,0 +1,353 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_linkagg
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Manage link aggregation groups on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of link aggregation groups
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the link aggregation group.
|
||||
required: true
|
||||
mode:
|
||||
description:
|
||||
- Mode of the link aggregation group. A value of C(on) will enable LACP.
|
||||
C(active) configures the link to actively information about the state of the link,
|
||||
or it can be configured in C(passive) mode ie. send link state information only when
|
||||
received them from another link.
|
||||
default: on
|
||||
choices: ['on', 'active', 'passive']
|
||||
members:
|
||||
description:
|
||||
- List of members interfaces of the link aggregation group. The value can be
|
||||
single interface or list of interfaces.
|
||||
required: true
|
||||
aggregate:
|
||||
description: List of link aggregation definitions.
|
||||
purge:
|
||||
description:
|
||||
- Purge link aggregation groups not defined in the I(aggregate) parameter.
|
||||
default: false
|
||||
type: bool
|
||||
state:
|
||||
description:
|
||||
- State of the link aggregation group.
|
||||
default: present
|
||||
choices: ['present', 'absent', 'up', 'down']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure link aggregation group
|
||||
onyx_linkagg:
|
||||
name: Po1
|
||||
members:
|
||||
- Eth1/1
|
||||
- Eth1/2
|
||||
|
||||
- name: remove configuration
|
||||
onyx_linkagg:
|
||||
name: Po1
|
||||
state: absent
|
||||
|
||||
- name: Create aggregate of linkagg definitions
|
||||
onyx_linkagg:
|
||||
aggregate:
|
||||
- { name: Po1, members: [Eth1/1] }
|
||||
- { name: Po2, members: [Eth1/2] }
|
||||
|
||||
- name: Remove aggregate of linkagg definitions
|
||||
onyx_linkagg:
|
||||
aggregate:
|
||||
- name: Po1
|
||||
- name: Po2
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device
|
||||
returned: always.
|
||||
type: list
|
||||
sample:
|
||||
- interface port-channel 1
|
||||
- exit
|
||||
- interface ethernet 1/1 channel-group 1 mode on
|
||||
- interface ethernet 1/2 channel-group 1 mode on
|
||||
"""
|
||||
|
||||
import re
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_interfaces_config
|
||||
|
||||
|
||||
class OnyxLinkAggModule(BaseOnyxModule):
|
||||
LAG_ID_REGEX = re.compile(r"^\d+ (Po\d+|Mpo\d+)\(([A-Z])\)$")
|
||||
LAG_NAME_REGEX = re.compile(r"^(Po|Mpo)(\d+)$")
|
||||
IF_NAME_REGEX = re.compile(r"^(Eth\d+\/\d+|Eth\d+\/\d+\/\d+)(.*)$")
|
||||
PORT_CHANNEL = 'port-channel'
|
||||
CHANNEL_GROUP = 'channel-group'
|
||||
MLAG_PORT_CHANNEL = 'mlag-port-channel'
|
||||
MLAG_CHANNEL_GROUP = 'mlag-channel-group'
|
||||
MLAG_SUMMARY = 'MLAG Port-Channel Summary'
|
||||
|
||||
LAG_TYPE = 'lag'
|
||||
MLAG_TYPE = 'mlag'
|
||||
|
||||
IF_TYPE_MAP = dict(
|
||||
lag=PORT_CHANNEL,
|
||||
mlag=MLAG_PORT_CHANNEL
|
||||
)
|
||||
|
||||
_purge = False
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
return dict(
|
||||
name=dict(type='str'),
|
||||
members=dict(type='list'),
|
||||
mode=dict(default='on', choices=['active', 'on', 'passive']),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_aggregate_spec(cls, element_spec):
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
return aggregate_spec
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
aggregate_spec = self._get_aggregate_spec(element_spec)
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict',
|
||||
options=aggregate_spec),
|
||||
purge=dict(default=False, type='bool'),
|
||||
)
|
||||
argument_spec.update(element_spec)
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate']]
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
def _get_lag_type(self, lag_name):
|
||||
match = self.LAG_NAME_REGEX.match(lag_name)
|
||||
if match:
|
||||
prefix = match.group(1)
|
||||
if prefix == "Po":
|
||||
return self.LAG_TYPE
|
||||
return self.MLAG_TYPE
|
||||
self._module.fail_json(
|
||||
msg='invalid lag name: %s, lag name should start with Po or '
|
||||
'Mpo' % lag_name)
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = list()
|
||||
module_params = self._module.params
|
||||
aggregate = module_params.get('aggregate')
|
||||
self._purge = module_params.get('purge', False)
|
||||
if aggregate:
|
||||
for item in aggregate:
|
||||
for key in item:
|
||||
if item.get(key) is None:
|
||||
item[key] = module_params[key]
|
||||
self.validate_param_values(item, item)
|
||||
req_item = item.copy()
|
||||
req_item['type'] = self._get_lag_type(req_item['name'])
|
||||
self._required_config.append(req_item)
|
||||
else:
|
||||
params = {
|
||||
'name': module_params['name'],
|
||||
'state': module_params['state'],
|
||||
'members': module_params['members'],
|
||||
'mode': module_params['mode'],
|
||||
'type': self._get_lag_type(module_params['name']),
|
||||
}
|
||||
self.validate_param_values(params)
|
||||
self._required_config.append(params)
|
||||
|
||||
@classmethod
|
||||
def _extract_lag_name(cls, header):
|
||||
match = cls.LAG_ID_REGEX.match(header)
|
||||
state = None
|
||||
lag_name = None
|
||||
if match:
|
||||
state = 'up' if match.group(2) == 'U' else 'down'
|
||||
lag_name = match.group(1)
|
||||
return lag_name, state
|
||||
|
||||
@classmethod
|
||||
def _extract_if_name(cls, member):
|
||||
match = cls.IF_NAME_REGEX.match(member)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
@classmethod
|
||||
def _extract_lag_members(cls, lag_type, lag_item):
|
||||
members = ""
|
||||
if lag_type == cls.LAG_TYPE:
|
||||
members = cls.get_config_attr(lag_item, "Member Ports")
|
||||
else:
|
||||
for attr_name, attr_val in iteritems(lag_item):
|
||||
if attr_name.startswith('Local Ports'):
|
||||
members = attr_val
|
||||
return [cls._extract_if_name(member) for member in members.split()]
|
||||
|
||||
def _get_port_channels(self, if_type):
|
||||
return get_interfaces_config(self._module, if_type, flags="summary")
|
||||
|
||||
def _parse_port_channels_summary(self, lag_type, lag_summary):
|
||||
if lag_type == self.MLAG_TYPE:
|
||||
if self._os_version >= self.ONYX_API_VERSION:
|
||||
found_summary = False
|
||||
for summary_item in lag_summary:
|
||||
if self.MLAG_SUMMARY in summary_item:
|
||||
lag_summary = summary_item[self.MLAG_SUMMARY]
|
||||
if lag_summary:
|
||||
lag_summary = lag_summary[0]
|
||||
else:
|
||||
lag_summary = dict()
|
||||
found_summary = True
|
||||
break
|
||||
if not found_summary:
|
||||
lag_summary = dict()
|
||||
else:
|
||||
lag_summary = lag_summary.get(self.MLAG_SUMMARY, dict())
|
||||
for lag_key, lag_data in iteritems(lag_summary):
|
||||
lag_name, state = self._extract_lag_name(lag_key)
|
||||
if not lag_name:
|
||||
continue
|
||||
lag_members = self._extract_lag_members(lag_type, lag_data[0])
|
||||
lag_obj = dict(
|
||||
name=lag_name,
|
||||
state=state,
|
||||
members=lag_members
|
||||
)
|
||||
self._current_config[lag_name] = lag_obj
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
self._os_version = self._get_os_version()
|
||||
lag_types = set([lag_obj['type'] for lag_obj in self._required_config])
|
||||
for lag_type in lag_types:
|
||||
if_type = self.IF_TYPE_MAP[lag_type]
|
||||
lag_summary = self._get_port_channels(if_type)
|
||||
if lag_summary:
|
||||
self._parse_port_channels_summary(lag_type, lag_summary)
|
||||
|
||||
def _get_interface_command_suffix(self, if_name):
|
||||
if if_name.startswith('Eth'):
|
||||
return if_name.replace("Eth", "ethernet ")
|
||||
if if_name.startswith('Po'):
|
||||
return if_name.replace("Po", "port-channel ")
|
||||
if if_name.startswith('Mpo'):
|
||||
return if_name.replace("Mpo", "mlag-port-channel ")
|
||||
self._module.fail_json(
|
||||
msg='invalid interface name: %s' % if_name)
|
||||
|
||||
def _get_channel_group(self, if_name):
|
||||
if if_name.startswith('Po'):
|
||||
return if_name.replace("Po", "channel-group ")
|
||||
if if_name.startswith('Mpo'):
|
||||
return if_name.replace("Mpo", "mlag-channel-group ")
|
||||
self._module.fail_json(
|
||||
msg='invalid interface name: %s' % if_name)
|
||||
|
||||
def _generate_no_linkagg_commands(self, lag_name):
|
||||
suffix = self._get_interface_command_suffix(lag_name)
|
||||
command = 'no interface %s' % suffix
|
||||
self._commands.append(command)
|
||||
|
||||
def _generate_linkagg_commands(self, lag_name, req_lag):
|
||||
curr_lag = self._current_config.get(lag_name, {})
|
||||
if not curr_lag:
|
||||
suffix = self._get_interface_command_suffix(lag_name)
|
||||
self._commands.append("interface %s" % suffix)
|
||||
self._commands.append("exit")
|
||||
curr_members = set(curr_lag.get('members', []))
|
||||
req_members = set(req_lag.get('members') or [])
|
||||
|
||||
lag_mode = req_lag['mode']
|
||||
if req_members != curr_members:
|
||||
channel_group = self._get_channel_group(lag_name)
|
||||
channel_group_type = channel_group.split()[0]
|
||||
for member in req_members:
|
||||
if member in curr_members:
|
||||
continue
|
||||
suffix = self._get_interface_command_suffix(member)
|
||||
self._commands.append(
|
||||
"interface %s %s mode %s" %
|
||||
(suffix, channel_group, lag_mode))
|
||||
for member in curr_members:
|
||||
if member in req_members:
|
||||
continue
|
||||
suffix = self._get_interface_command_suffix(member)
|
||||
self._commands.append(
|
||||
"interface %s no %s" % (suffix, channel_group_type))
|
||||
req_state = req_lag.get('state')
|
||||
if req_state in ('up', 'down'):
|
||||
curr_state = curr_lag.get('state')
|
||||
if curr_state != req_state:
|
||||
suffix = self._get_interface_command_suffix(lag_name)
|
||||
cmd = "interface %s " % suffix
|
||||
if req_state == 'up':
|
||||
cmd += 'no shutdown'
|
||||
else:
|
||||
cmd += 'shutdown'
|
||||
self._commands.append(cmd)
|
||||
|
||||
def generate_commands(self):
|
||||
req_lags = set()
|
||||
for req_conf in self._required_config:
|
||||
state = req_conf['state']
|
||||
lag_name = req_conf['name']
|
||||
if state == 'absent':
|
||||
if lag_name in self._current_config:
|
||||
self._generate_no_linkagg_commands(lag_name)
|
||||
else:
|
||||
req_lags.add(lag_name)
|
||||
self._generate_linkagg_commands(lag_name, req_conf)
|
||||
if self._purge:
|
||||
for lag_name in self._current_config:
|
||||
if lag_name not in req_lags:
|
||||
self._generate_no_linkagg_commands(lag_name)
|
||||
|
||||
def check_declarative_intent_params(self, result):
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxLinkAggModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
116
plugins/modules/network/onyx/onyx_lldp.py
Normal file
116
plugins/modules/network/onyx/onyx_lldp.py
Normal file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_lldp
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Manage LLDP configuration on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of LLDP service configuration
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- State of the LLDP protocol configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Enable LLDP protocol
|
||||
onyx_lldp:
|
||||
state: present
|
||||
|
||||
- name: Disable LLDP protocol
|
||||
onyx_lldp:
|
||||
state: lldp
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device
|
||||
returned: always.
|
||||
type: list
|
||||
sample:
|
||||
- lldp
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxLldpModule(BaseOnyxModule):
|
||||
LLDP_ENTRY = 'LLDP'
|
||||
SHOW_LLDP_CMD = 'show lldp local'
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
return dict(
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
)
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = dict()
|
||||
module_params = self._module.params
|
||||
params = {
|
||||
'state': module_params['state'],
|
||||
}
|
||||
|
||||
self.validate_param_values(params)
|
||||
self._required_config.update(params)
|
||||
|
||||
def _get_lldp_config(self):
|
||||
return show_cmd(self._module, self.SHOW_LLDP_CMD)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
state = 'absent'
|
||||
config = self._get_lldp_config() or dict()
|
||||
for item in config:
|
||||
lldp_state = item.get(self.LLDP_ENTRY)
|
||||
if lldp_state is not None:
|
||||
if lldp_state == 'enabled':
|
||||
state = 'present'
|
||||
break
|
||||
self._current_config['state'] = state
|
||||
|
||||
def generate_commands(self):
|
||||
req_state = self._required_config['state']
|
||||
curr_state = self._current_config['state']
|
||||
if curr_state != req_state:
|
||||
cmd = 'lldp'
|
||||
if req_state == 'absent':
|
||||
cmd = 'no %s' % cmd
|
||||
self._commands.append(cmd)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxLldpModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
228
plugins/modules/network/onyx/onyx_lldp_interface.py
Normal file
228
plugins/modules/network/onyx/onyx_lldp_interface.py
Normal file
@@ -0,0 +1,228 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_lldp_interface
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Manage LLDP interfaces configuration on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of LLDP interfaces
|
||||
configuration on Mellanox ONYX network devices.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the interface LLDP should be configured on.
|
||||
aggregate:
|
||||
description: List of interfaces LLDP should be configured on.
|
||||
purge:
|
||||
description:
|
||||
- Purge interfaces not defined in the aggregate parameter.
|
||||
type: bool
|
||||
default: false
|
||||
state:
|
||||
description:
|
||||
- State of the LLDP configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent', 'enabled', 'disabled']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Configure LLDP on specific interfaces
|
||||
onyx_lldp_interface:
|
||||
name: Eth1/1
|
||||
state: present
|
||||
|
||||
- name: Disable LLDP on specific interfaces
|
||||
onyx_lldp_interface:
|
||||
name: Eth1/1
|
||||
state: disabled
|
||||
|
||||
- name: Enable LLDP on specific interfaces
|
||||
onyx_lldp_interface:
|
||||
name: Eth1/1
|
||||
state: enabled
|
||||
|
||||
- name: Delete LLDP on specific interfaces
|
||||
onyx_lldp_interface:
|
||||
name: Eth1/1
|
||||
state: absent
|
||||
|
||||
- name: Create aggregate of LLDP interface configurations
|
||||
onyx_lldp_interface:
|
||||
aggregate:
|
||||
- { name: Eth1/1 }
|
||||
- { name: Eth1/2 }
|
||||
state: present
|
||||
|
||||
- name: Delete aggregate of LLDP interface configurations
|
||||
onyx_lldp_interface:
|
||||
aggregate:
|
||||
- { name: Eth1/1 }
|
||||
- { name: Eth1/2 }
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device
|
||||
returned: always.
|
||||
type: list
|
||||
sample:
|
||||
- interface ethernet 1/1 lldp transmit
|
||||
- interface ethernet 1/1 lldp receive
|
||||
"""
|
||||
import re
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxLldpInterfaceModule(BaseOnyxModule):
|
||||
IF_NAME_REGEX = re.compile(r"^(Eth\d+\/\d+|Eth\d+\/\d+\d+)$")
|
||||
_purge = False
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
return dict(
|
||||
name=dict(type='str'),
|
||||
state=dict(default='present',
|
||||
choices=['present', 'absent', 'enabled', 'disabled']),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_aggregate_spec(cls, element_spec):
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
return aggregate_spec
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
aggregate_spec = self._get_aggregate_spec(element_spec)
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict',
|
||||
options=aggregate_spec),
|
||||
purge=dict(default=False, type='bool'),
|
||||
)
|
||||
argument_spec.update(element_spec)
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate']]
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = list()
|
||||
module_params = self._module.params
|
||||
aggregate = module_params.get('aggregate')
|
||||
self._purge = module_params.get('purge', False)
|
||||
if aggregate:
|
||||
for item in aggregate:
|
||||
for key in item:
|
||||
if item.get(key) is None:
|
||||
item[key] = module_params[key]
|
||||
self.validate_param_values(item, item)
|
||||
req_item = item.copy()
|
||||
self._required_config.append(req_item)
|
||||
else:
|
||||
params = {
|
||||
'name': module_params['name'],
|
||||
'state': module_params['state'],
|
||||
}
|
||||
self.validate_param_values(params)
|
||||
self._required_config.append(params)
|
||||
|
||||
def _create_if_lldp_data(self, if_name, if_lldp_data):
|
||||
return {
|
||||
'name': if_name,
|
||||
'receive': self.get_config_attr(if_lldp_data, 'Receive'),
|
||||
'transmit': self.get_config_attr(if_lldp_data, 'Transmit'),
|
||||
}
|
||||
|
||||
def _get_lldp_config(self):
|
||||
return show_cmd(self._module, "show lldp interfaces")
|
||||
|
||||
def load_current_config(self):
|
||||
# called in base class in run function
|
||||
self._current_config = dict()
|
||||
lldp_config = self._get_lldp_config()
|
||||
if not lldp_config:
|
||||
return
|
||||
for if_name, if_lldp_data in iteritems(lldp_config):
|
||||
match = self.IF_NAME_REGEX.match(if_name)
|
||||
if not match:
|
||||
continue
|
||||
if if_lldp_data:
|
||||
if_lldp_data = if_lldp_data[0]
|
||||
self._current_config[if_name] = \
|
||||
self._create_if_lldp_data(if_name, if_lldp_data)
|
||||
|
||||
def _get_interface_cmd_name(self, if_name):
|
||||
return if_name.replace("Eth", "ethernet ")
|
||||
|
||||
def _add_if_lldp_commands(self, if_name, flag, enable):
|
||||
cmd_prefix = "interface %s " % self._get_interface_cmd_name(if_name)
|
||||
lldp_cmd = "lldp %s" % flag
|
||||
if not enable:
|
||||
lldp_cmd = 'no %s' % lldp_cmd
|
||||
self._commands.append(cmd_prefix + lldp_cmd)
|
||||
|
||||
def _gen_lldp_commands(self, if_name, req_state, curr_conf):
|
||||
curr_receive = curr_conf.get('receive')
|
||||
curr_transmit = curr_conf.get('transmit')
|
||||
enable = (req_state == 'Enabled')
|
||||
if curr_receive != req_state:
|
||||
flag = 'receive'
|
||||
self._add_if_lldp_commands(if_name, flag, enable)
|
||||
if curr_transmit != req_state:
|
||||
flag = 'transmit'
|
||||
self._add_if_lldp_commands(if_name, flag, enable)
|
||||
|
||||
def generate_commands(self):
|
||||
req_interfaces = set()
|
||||
for req_conf in self._required_config:
|
||||
state = req_conf['state']
|
||||
if_name = req_conf['name']
|
||||
if state in ('absent', 'disabled'):
|
||||
req_state = 'Disabled'
|
||||
else:
|
||||
req_interfaces.add(if_name)
|
||||
req_state = 'Enabled'
|
||||
curr_conf = self._current_config.get(if_name, {})
|
||||
self._gen_lldp_commands(if_name, req_state, curr_conf)
|
||||
if self._purge:
|
||||
for if_name, curr_conf in iteritems(self._current_config):
|
||||
if if_name not in req_interfaces:
|
||||
req_state = 'Disabled'
|
||||
self._gen_lldp_commands(if_name, req_state, curr_conf)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxLldpInterfaceModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
235
plugins/modules/network/onyx/onyx_magp.py
Normal file
235
plugins/modules/network/onyx/onyx_magp.py
Normal file
@@ -0,0 +1,235 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_magp
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Manage MAGP protocol on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of MAGP protocol on vlan
|
||||
interface of Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.4000
|
||||
options:
|
||||
magp_id:
|
||||
description:
|
||||
- "MAGP instance number 1-255"
|
||||
required: true
|
||||
interface:
|
||||
description:
|
||||
- VLAN Interface name.
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- MAGP state.
|
||||
default: present
|
||||
choices: ['present', 'absent', 'enabled', 'disabled']
|
||||
router_ip:
|
||||
description:
|
||||
- MAGP router IP address.
|
||||
router_mac:
|
||||
description:
|
||||
- MAGP router MAC address.
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: run add vlan interface with magp
|
||||
onyx_magp:
|
||||
magp_id: 103
|
||||
router_ip: 192.168.8.2
|
||||
router_mac: AA:1B:2C:3D:4E:5F
|
||||
interface: Vlan 1002
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- interface vlan 234 magp 103
|
||||
- exit
|
||||
- interface vlan 234 magp 103 ip virtual-router address 1.2.3.4
|
||||
"""
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxMagpModule(BaseOnyxModule):
|
||||
IF_VLAN_REGEX = re.compile(r"^Vlan (\d+)$")
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
return dict(
|
||||
magp_id=dict(type='int', required=True),
|
||||
state=dict(default='present',
|
||||
choices=['present', 'absent', 'enabled', 'disabled']),
|
||||
interface=dict(required=True),
|
||||
router_ip=dict(),
|
||||
router_mac=dict(),
|
||||
)
|
||||
|
||||
def init_module(self):
|
||||
""" Ansible module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def validate_magp_id(self, value):
|
||||
if value and not 1 <= int(value) <= 255:
|
||||
self._module.fail_json(msg='magp id must be between 1 and 255')
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
interface = module_params['interface']
|
||||
match = self.IF_VLAN_REGEX.match(interface)
|
||||
vlan_id = 0
|
||||
if match:
|
||||
vlan_id = int(match.group(1))
|
||||
else:
|
||||
self._module.fail_json(
|
||||
msg='Invalid interface name: should be "Vlan <vlan_id>"')
|
||||
|
||||
self._required_config = dict(
|
||||
magp_id=module_params['magp_id'],
|
||||
state=module_params['state'],
|
||||
vlan_id=vlan_id,
|
||||
router_ip=module_params['router_ip'],
|
||||
router_mac=module_params['router_mac'])
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
@classmethod
|
||||
def get_magp_id(cls, item):
|
||||
header = cls.get_config_attr(item, "header")
|
||||
return int(header.split()[1])
|
||||
|
||||
def _create_magp_instance_data(self, magp_id, item):
|
||||
vlan_id = int(self.get_config_attr(item, "Interface vlan"))
|
||||
state = self.get_config_attr(item, "Admin state").lower()
|
||||
return dict(
|
||||
magp_id=magp_id,
|
||||
state=state,
|
||||
vlan_id=vlan_id,
|
||||
router_ip=self.get_config_attr(item, "Virtual IP"),
|
||||
router_mac=self.get_config_attr(item, "Virtual MAC"))
|
||||
|
||||
def _update_magp_data(self, magp_data):
|
||||
if self._os_version >= self.ONYX_API_VERSION:
|
||||
for magp_config in magp_data:
|
||||
for magp_name, data in iteritems(magp_config):
|
||||
magp_id = int(magp_name.replace('MAGP ', ''))
|
||||
self._current_config[magp_id] = \
|
||||
self._create_magp_instance_data(magp_id, data[0])
|
||||
else:
|
||||
for magp_item in magp_data:
|
||||
magp_id = self.get_magp_id(magp_item)
|
||||
inst_data = self._create_magp_instance_data(magp_id, magp_item)
|
||||
self._current_config[magp_id] = inst_data
|
||||
|
||||
def _get_magp_config(self):
|
||||
cmd = "show magp"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
# called in base class in run function
|
||||
self._os_version = self._get_os_version()
|
||||
self._current_config = dict()
|
||||
magp_data = self._get_magp_config()
|
||||
if magp_data:
|
||||
self._update_magp_data(magp_data)
|
||||
|
||||
def _generate_no_magp_commands(self):
|
||||
req_vlan_id = self._required_config['vlan_id']
|
||||
req_magp_id = self._required_config['magp_id']
|
||||
curr_magp_data = self._current_config.get(req_magp_id)
|
||||
if not curr_magp_data:
|
||||
return
|
||||
curr_vlan_id = curr_magp_data.get(req_vlan_id)
|
||||
if curr_vlan_id == req_vlan_id:
|
||||
cmd = 'interface vlan %s no magp %s' % (req_vlan_id, req_magp_id)
|
||||
self._commands.append(cmd)
|
||||
|
||||
def _generate_magp_commands(self, req_state):
|
||||
req_vlan_id = self._required_config['vlan_id']
|
||||
req_magp_id = self._required_config['magp_id']
|
||||
curr_magp_data = self._current_config.get(req_magp_id, dict())
|
||||
curr_vlan_id = curr_magp_data.get('vlan_id')
|
||||
magp_prefix = 'interface vlan %s magp %s' % (req_vlan_id, req_magp_id)
|
||||
create_new_magp = False
|
||||
if curr_vlan_id != req_vlan_id:
|
||||
if curr_vlan_id:
|
||||
cmd = 'interface vlan %s no magp %s' % (
|
||||
curr_vlan_id, req_magp_id)
|
||||
self._commands.append(cmd)
|
||||
create_new_magp = True
|
||||
self._commands.append(magp_prefix)
|
||||
self._commands.append('exit')
|
||||
req_router_ip = self._required_config['router_ip']
|
||||
curr_router_ip = curr_magp_data.get('router_ip')
|
||||
if req_router_ip:
|
||||
if curr_router_ip != req_router_ip or create_new_magp:
|
||||
cmd = '%s ip virtual-router address %s' % (
|
||||
magp_prefix, req_router_ip)
|
||||
self._commands.append(cmd)
|
||||
else:
|
||||
if curr_router_ip and curr_router_ip != '0.0.0.0':
|
||||
cmd = '%s no ip virtual-router address' % magp_prefix
|
||||
self._commands.append(cmd)
|
||||
req_router_mac = self._required_config['router_mac']
|
||||
curr_router_mac = curr_magp_data.get('router_mac')
|
||||
if curr_router_mac:
|
||||
curr_router_mac = curr_router_mac.lower()
|
||||
if req_router_mac:
|
||||
req_router_mac = req_router_mac.lower()
|
||||
if curr_router_mac != req_router_mac or create_new_magp:
|
||||
cmd = '%s ip virtual-router mac-address %s' % (
|
||||
magp_prefix, req_router_mac)
|
||||
self._commands.append(cmd)
|
||||
else:
|
||||
if curr_router_mac and curr_router_mac != '00:00:00:00:00:00':
|
||||
cmd = '%s no ip virtual-router mac-address' % magp_prefix
|
||||
self._commands.append(cmd)
|
||||
if req_state in ('enabled', 'disabled'):
|
||||
curr_state = curr_magp_data.get('state', 'enabled')
|
||||
if curr_state != req_state:
|
||||
if req_state == 'enabled':
|
||||
suffix = 'no shutdown'
|
||||
else:
|
||||
suffix = 'shutdown'
|
||||
cmd = '%s %s' % (magp_prefix, suffix)
|
||||
self._commands.append(cmd)
|
||||
|
||||
def generate_commands(self):
|
||||
req_state = self._required_config['state']
|
||||
if req_state == 'absent':
|
||||
return self._generate_no_magp_commands()
|
||||
return self._generate_magp_commands(req_state)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxMagpModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
209
plugins/modules/network/onyx/onyx_mlag_ipl.py
Normal file
209
plugins/modules/network/onyx/onyx_mlag_ipl.py
Normal file
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_mlag_ipl
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Manage IPL (inter-peer link) on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of IPL (inter-peer link)
|
||||
management on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.4000
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the interface (port-channel) IPL should be configured on.
|
||||
required: true
|
||||
vlan_interface:
|
||||
description:
|
||||
- Name of the IPL vlan interface.
|
||||
state:
|
||||
description:
|
||||
- IPL state.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
peer_address:
|
||||
description:
|
||||
- IPL peer IP address.
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: run configure ipl
|
||||
onyx_mlag_ipl:
|
||||
name: Po1
|
||||
vlan_interface: Vlan 322
|
||||
state: present
|
||||
peer_address: 192.168.7.1
|
||||
|
||||
- name: run remove ipl
|
||||
onyx_mlag_ipl:
|
||||
name: Po1
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- interface port-channel 1 ipl 1
|
||||
- interface vlan 1024 ipl 1 peer-address 10.10.10.10
|
||||
"""
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxMlagIplModule(BaseOnyxModule):
|
||||
VLAN_IF_REGEX = re.compile(r'^Vlan \d+')
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
return dict(
|
||||
name=dict(required=True),
|
||||
state=dict(default='present',
|
||||
choices=['present', 'absent']),
|
||||
peer_address=dict(),
|
||||
vlan_interface=dict(),
|
||||
)
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(
|
||||
name=module_params['name'],
|
||||
state=module_params['state'],
|
||||
peer_address=module_params['peer_address'],
|
||||
vlan_interface=module_params['vlan_interface'])
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _update_mlag_data(self, mlag_data):
|
||||
if not mlag_data:
|
||||
return
|
||||
mlag_summary = mlag_data.get("MLAG IPLs Summary", {})
|
||||
ipl_id = "1"
|
||||
ipl_list = mlag_summary.get(ipl_id)
|
||||
if ipl_list:
|
||||
ipl_data = ipl_list[0]
|
||||
vlan_id = ipl_data.get("Vlan Interface")
|
||||
vlan_interface = ""
|
||||
if vlan_id != "N/A":
|
||||
vlan_interface = "Vlan %s" % vlan_id
|
||||
peer_address = ipl_data.get("Peer IP address")
|
||||
name = ipl_data.get("Group Port-Channel")
|
||||
self._current_config = dict(
|
||||
name=name,
|
||||
peer_address=peer_address,
|
||||
vlan_interface=vlan_interface)
|
||||
|
||||
def _show_mlag_data(self):
|
||||
cmd = "show mlag"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
# called in base class in run function
|
||||
self._current_config = dict()
|
||||
mlag_data = self._show_mlag_data()
|
||||
self._update_mlag_data(mlag_data)
|
||||
|
||||
def _get_interface_cmd_name(self, if_name):
|
||||
if if_name.startswith('Po'):
|
||||
return if_name.replace("Po", "port-channel ")
|
||||
self._module.fail_json(
|
||||
msg='invalid interface name: %s' % if_name)
|
||||
|
||||
def _generate_port_channel_command(self, if_name, enable):
|
||||
if_cmd_name = self._get_interface_cmd_name(if_name)
|
||||
if enable:
|
||||
ipl_cmd = 'ipl 1'
|
||||
else:
|
||||
ipl_cmd = "no ipl 1"
|
||||
cmd = "interface %s %s" % (if_cmd_name, ipl_cmd)
|
||||
return cmd
|
||||
|
||||
def _generate_vlan_if_command(self, if_name, enable, peer_address):
|
||||
if_cmd_name = if_name.lower()
|
||||
if enable:
|
||||
ipl_cmd = 'ipl 1 peer-address %s' % peer_address
|
||||
else:
|
||||
ipl_cmd = "no ipl 1"
|
||||
cmd = "interface %s %s" % (if_cmd_name, ipl_cmd)
|
||||
return cmd
|
||||
|
||||
def _generate_no_ipl_commands(self):
|
||||
curr_interface = self._current_config.get('name')
|
||||
req_interface = self._required_config.get('name')
|
||||
if curr_interface == req_interface:
|
||||
cmd = self._generate_port_channel_command(
|
||||
req_interface, enable=False)
|
||||
self._commands.append(cmd)
|
||||
|
||||
def _generate_ipl_commands(self):
|
||||
curr_interface = self._current_config.get('name')
|
||||
req_interface = self._required_config.get('name')
|
||||
if curr_interface != req_interface:
|
||||
if curr_interface and curr_interface != 'N/A':
|
||||
cmd = self._generate_port_channel_command(
|
||||
curr_interface, enable=False)
|
||||
self._commands.append(cmd)
|
||||
cmd = self._generate_port_channel_command(
|
||||
req_interface, enable=True)
|
||||
self._commands.append(cmd)
|
||||
curr_vlan = self._current_config.get('vlan_interface')
|
||||
req_vlan = self._required_config.get('vlan_interface')
|
||||
add_peer = False
|
||||
if curr_vlan != req_vlan:
|
||||
add_peer = True
|
||||
if curr_vlan:
|
||||
cmd = self._generate_vlan_if_command(curr_vlan, enable=False,
|
||||
peer_address=None)
|
||||
self._commands.append(cmd)
|
||||
curr_peer = self._current_config.get('peer_address')
|
||||
req_peer = self._required_config.get('peer_address')
|
||||
if req_peer != curr_peer:
|
||||
add_peer = True
|
||||
if add_peer and req_peer:
|
||||
cmd = self._generate_vlan_if_command(req_vlan, enable=True,
|
||||
peer_address=req_peer)
|
||||
self._commands.append(cmd)
|
||||
|
||||
def generate_commands(self):
|
||||
state = self._required_config['state']
|
||||
if state == 'absent':
|
||||
self._generate_no_ipl_commands()
|
||||
else:
|
||||
self._generate_ipl_commands()
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxMlagIplModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
184
plugins/modules/network/onyx/onyx_mlag_vip.py
Normal file
184
plugins/modules/network/onyx/onyx_mlag_vip.py
Normal file
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_mlag_vip
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Configures MLAG VIP on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of MLAG virtual IPs
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.4000
|
||||
options:
|
||||
ipaddress:
|
||||
description:
|
||||
- Virtual IP address of the MLAG. Required if I(state=present).
|
||||
group_name:
|
||||
description:
|
||||
- MLAG group name. Required if I(state=present).
|
||||
mac_address:
|
||||
description:
|
||||
- MLAG system MAC address. Required if I(state=present).
|
||||
state:
|
||||
description:
|
||||
- MLAG VIP state.
|
||||
choices: ['present', 'absent']
|
||||
delay:
|
||||
description:
|
||||
- Delay interval, in seconds, waiting for the changes on mlag VIP to take
|
||||
effect.
|
||||
default: 12
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure mlag-vip
|
||||
onyx_mlag_vip:
|
||||
ipaddress: 50.3.3.1/24
|
||||
group_name: ansible-test-group
|
||||
mac_address: 00:11:12:23:34:45
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- mlag-vip ansible_test_group ip 50.3.3.1 /24 force
|
||||
- no mlag shutdown
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxMLagVipModule(BaseOnyxModule):
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
element_spec = dict(
|
||||
ipaddress=dict(),
|
||||
group_name=dict(),
|
||||
mac_address=dict(),
|
||||
delay=dict(type='int', default=12),
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
)
|
||||
argument_spec = dict()
|
||||
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
lag_params = {
|
||||
'ipaddress': module_params['ipaddress'],
|
||||
'group_name': module_params['group_name'],
|
||||
'mac_address': module_params['mac_address'],
|
||||
'delay': module_params['delay'],
|
||||
'state': module_params['state'],
|
||||
}
|
||||
|
||||
self.validate_param_values(lag_params)
|
||||
self._required_config = lag_params
|
||||
|
||||
def _show_mlag_cmd(self, cmd):
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def _show_mlag(self):
|
||||
cmd = "show mlag"
|
||||
return self._show_mlag_cmd(cmd)
|
||||
|
||||
def _show_mlag_vip(self):
|
||||
cmd = "show mlag-vip"
|
||||
return self._show_mlag_cmd(cmd)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
mlag_config = self._show_mlag()
|
||||
mlag_vip_config = self._show_mlag_vip()
|
||||
if mlag_vip_config:
|
||||
mlag_vip = mlag_vip_config.get("MLAG-VIP", {})
|
||||
self._current_config['group_name'] = \
|
||||
mlag_vip.get("MLAG group name")
|
||||
self._current_config['ipaddress'] = \
|
||||
mlag_vip.get("MLAG VIP address")
|
||||
if mlag_config:
|
||||
self._current_config['mac_address'] = \
|
||||
mlag_config.get("System-mac")
|
||||
|
||||
def generate_commands(self):
|
||||
state = self._required_config['state']
|
||||
if state == 'present':
|
||||
self._generate_mlag_vip_cmds()
|
||||
else:
|
||||
self._generate_no_mlag_vip_cmds()
|
||||
|
||||
def _generate_mlag_vip_cmds(self):
|
||||
current_group = self._current_config.get('group_name')
|
||||
current_ip = self._current_config.get('ipaddress')
|
||||
current_mac = self._current_config.get('mac_address')
|
||||
if current_mac:
|
||||
current_mac = current_mac.lower()
|
||||
|
||||
req_group = self._required_config.get('group_name')
|
||||
req_ip = self._required_config.get('ipaddress')
|
||||
req_mac = self._required_config.get('mac_address')
|
||||
if req_mac:
|
||||
req_mac = req_mac.lower()
|
||||
|
||||
if req_ip is not None:
|
||||
if req_group is None:
|
||||
self._module.fail_json(msg='In order to configure Mlag-Vip you must send '
|
||||
'group name param beside IPaddress')
|
||||
ipaddr, mask = req_ip.split('/')
|
||||
if req_group != current_group or req_ip != current_ip:
|
||||
self._commands.append('mlag-vip %s ip %s /%s force' % (req_group, ipaddr, mask))
|
||||
elif req_group and req_group != current_group:
|
||||
self._commands.append('mlag-vip %s' % req_group)
|
||||
|
||||
if req_mac and req_mac != current_mac:
|
||||
self._commands.append(
|
||||
'mlag system-mac %s' % (req_mac))
|
||||
if self._commands:
|
||||
self._commands.append('no mlag shutdown')
|
||||
|
||||
def _generate_no_mlag_vip_cmds(self):
|
||||
if self._current_config.get('group_name'):
|
||||
self._commands.append('no mlag-vip')
|
||||
|
||||
def check_declarative_intent_params(self, result):
|
||||
if not result['changed']:
|
||||
return
|
||||
delay_interval = self._required_config.get('delay')
|
||||
if delay_interval > 0:
|
||||
time.sleep(delay_interval)
|
||||
for cmd in ("show mlag-vip", ""):
|
||||
show_cmd(self._module, cmd, json_fmt=False, fail_on_error=False)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxMLagVipModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
242
plugins/modules/network/onyx/onyx_ntp.py
Normal file
242
plugins/modules/network/onyx/onyx_ntp.py
Normal file
@@ -0,0 +1,242 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_ntp
|
||||
author: "Sara-Touqan (@sarato)"
|
||||
short_description: Manage NTP general configurations and ntp keys configurations on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of NTP & NTP Keys
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- State of the NTP configuration.
|
||||
choices: ['enabled', 'disabled']
|
||||
type: str
|
||||
authenticate_state:
|
||||
description:
|
||||
- State of the NTP authentication configuration.
|
||||
choices: ['enabled', 'disabled']
|
||||
type: str
|
||||
ntp_authentication_keys:
|
||||
type: list
|
||||
description:
|
||||
- List of ntp authentication keys
|
||||
suboptions:
|
||||
auth_key_id:
|
||||
description:
|
||||
- Configures ntp key-id, range 1-65534
|
||||
required: true
|
||||
type: int
|
||||
auth_key_encrypt_type:
|
||||
description:
|
||||
- encryption type used to configure ntp authentication key.
|
||||
required: true
|
||||
choices: ['md5', 'sha1']
|
||||
type: str
|
||||
auth_key_password:
|
||||
description:
|
||||
- password used for ntp authentication key.
|
||||
required: true
|
||||
type: str
|
||||
auth_key_state:
|
||||
description:
|
||||
- Used to decide if you want to delete given ntp key or not
|
||||
choices: ['present', 'absent']
|
||||
type: str
|
||||
trusted_keys:
|
||||
type: list
|
||||
description:
|
||||
- List of ntp trusted keys
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure NTP
|
||||
onyx_ntp:
|
||||
state: enabled
|
||||
authenticate_state: enabled
|
||||
ntp_authentication_keys:
|
||||
- auth_key_id: 1
|
||||
auth_key_encrypt_type: md5
|
||||
auth_key_password: 12345
|
||||
auth_key_state: absent
|
||||
trusted_keys: 1,2,3
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device
|
||||
returned: always.
|
||||
type: list
|
||||
sample:
|
||||
- ntp enable
|
||||
- ntp disable
|
||||
- ntp authenticate
|
||||
- no ntp authenticate
|
||||
- ntp authentication-key 1 md5 12345
|
||||
- no ntp authentication-key 1
|
||||
- ntp trusted-key 1,2,3
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxNTPModule(BaseOnyxModule):
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
ntp_authentication_key_spec = dict(auth_key_id=dict(type='int', required=True),
|
||||
auth_key_encrypt_type=dict(required=True, choices=['md5', 'sha1']),
|
||||
auth_key_password=dict(required=True),
|
||||
auth_key_state=dict(choices=['present', 'absent']))
|
||||
element_spec = dict(
|
||||
state=dict(choices=['enabled', 'disabled']),
|
||||
authenticate_state=dict(choices=['enabled', 'disabled']),
|
||||
ntp_authentication_keys=dict(type='list', elements='dict', options=ntp_authentication_key_spec),
|
||||
trusted_keys=dict(type='list', elements='int')
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def _validate_key_id(self):
|
||||
keys_id_list = self._required_config.get("ntp_authentication_keys")
|
||||
if keys_id_list:
|
||||
for key_item in keys_id_list:
|
||||
key_id = key_item.get("auth_key_id")
|
||||
if (key_id < 1) or (key_id > 65534):
|
||||
self._module.fail_json(
|
||||
msg='Invalid Key value, value should be in the range 1-65534')
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
self._validate_key_id()
|
||||
|
||||
def _show_ntp_config(self):
|
||||
show_cmds = []
|
||||
cmd = "show ntp"
|
||||
show_cmds.append(show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False))
|
||||
cmd = "show ntp keys"
|
||||
show_cmds.append(show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False))
|
||||
return show_cmds
|
||||
|
||||
def _set_ntp_keys_config(self, ntp_config):
|
||||
if not ntp_config:
|
||||
return
|
||||
for req_ntp_auth_key in ntp_config:
|
||||
ecryption_type = req_ntp_auth_key.get("Encryption Type")
|
||||
self._current_config[req_ntp_auth_key.get("header")] = ecryption_type
|
||||
|
||||
def _set_ntp_config(self, ntp_config):
|
||||
ntp_config = ntp_config[0]
|
||||
if not ntp_config:
|
||||
return
|
||||
self._current_config['state'] = ntp_config.get("NTP is administratively")
|
||||
self._current_config['authenticate_state'] = ntp_config.get("NTP Authentication administratively")
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
ntp_config = self._show_ntp_config()
|
||||
if ntp_config:
|
||||
if ntp_config[0]:
|
||||
self._set_ntp_config(ntp_config[0])
|
||||
if ntp_config[1]:
|
||||
self._set_ntp_keys_config(ntp_config[1])
|
||||
|
||||
def generate_commands(self):
|
||||
current_state = self._current_config.get("state")
|
||||
state = self._required_config.get("state")
|
||||
if state is None:
|
||||
state = current_state
|
||||
if state is not None:
|
||||
if current_state != state:
|
||||
if state == 'enabled':
|
||||
self._commands.append('ntp enable')
|
||||
else:
|
||||
self._commands.append('no ntp enable')
|
||||
authenticate_state = self._required_config.get("authenticate_state")
|
||||
if authenticate_state:
|
||||
current_authenticate_state = self._current_config.get("authenticate_state")
|
||||
if authenticate_state is not None:
|
||||
if current_authenticate_state != authenticate_state:
|
||||
if authenticate_state == 'enabled':
|
||||
self._commands.append('ntp authenticate')
|
||||
else:
|
||||
self._commands.append('no ntp authenticate')
|
||||
req_ntp_auth_keys = self._required_config.get('ntp_authentication_keys')
|
||||
if req_ntp_auth_keys:
|
||||
if req_ntp_auth_keys is not None:
|
||||
for req_ntp_auth_key in req_ntp_auth_keys:
|
||||
req_key_id = req_ntp_auth_key.get('auth_key_id')
|
||||
req_key = 'NTP Key ' + str(req_key_id)
|
||||
current_req_key = self._current_config.get(req_key)
|
||||
auth_key_state = req_ntp_auth_key.get('auth_key_state')
|
||||
req_encrypt_type = req_ntp_auth_key.get('auth_key_encrypt_type')
|
||||
req_password = req_ntp_auth_key.get('auth_key_password')
|
||||
if current_req_key:
|
||||
if req_encrypt_type == current_req_key:
|
||||
if auth_key_state:
|
||||
if auth_key_state == 'absent':
|
||||
self._commands.append('no ntp authentication-key {0}' .format(req_key_id))
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
if auth_key_state:
|
||||
if auth_key_state == 'present':
|
||||
self._commands.append('ntp authentication-key {0} {1} {2}'
|
||||
.format(req_key_id,
|
||||
req_encrypt_type,
|
||||
req_password))
|
||||
else:
|
||||
self._commands.append('ntp authentication-key {0} {1} {2}'
|
||||
.format(req_key_id,
|
||||
req_encrypt_type,
|
||||
req_password))
|
||||
|
||||
else:
|
||||
if auth_key_state:
|
||||
if auth_key_state == 'present':
|
||||
self._commands.append('ntp authentication-key {0} {1} {2}'
|
||||
.format(req_key_id,
|
||||
req_encrypt_type,
|
||||
req_password))
|
||||
else:
|
||||
self._commands.append('ntp authentication-key {0} {1} {2}'
|
||||
.format(req_key_id,
|
||||
req_encrypt_type,
|
||||
req_password))
|
||||
|
||||
req_trusted_keys = self._required_config.get('trusted_keys')
|
||||
if req_trusted_keys:
|
||||
for key in req_trusted_keys:
|
||||
self._commands.append('ntp trusted-key {0}' .format(key))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxNTPModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
285
plugins/modules/network/onyx/onyx_ntp_servers_peers.py
Normal file
285
plugins/modules/network/onyx/onyx_ntp_servers_peers.py
Normal file
@@ -0,0 +1,285 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_ntp_servers_peers
|
||||
author: "Sara-Touqan (@sarato)"
|
||||
short_description: Configures NTP peers and servers parameters
|
||||
description:
|
||||
- This module provides declarative management of NTP peers and servers configuration on Mellanox ONYX network devices.
|
||||
options:
|
||||
peer:
|
||||
type: list
|
||||
description:
|
||||
- List of ntp peers.
|
||||
suboptions:
|
||||
ip_or_name:
|
||||
description:
|
||||
- Configures ntp peer name or ip.
|
||||
required: true
|
||||
type: str
|
||||
enabled:
|
||||
description:
|
||||
- Disables/Enables ntp peer state
|
||||
type: bool
|
||||
version:
|
||||
description:
|
||||
- version number for the ntp peer
|
||||
choices: [3, 4]
|
||||
type: int
|
||||
key_id:
|
||||
description:
|
||||
- Used to configure the key-id for the ntp peer
|
||||
type: int
|
||||
state:
|
||||
description:
|
||||
- Indicates if the ntp peer exists or should be deleted
|
||||
choices: ['present', 'absent']
|
||||
type: str
|
||||
server:
|
||||
type: list
|
||||
description:
|
||||
- List of ntp servers.
|
||||
suboptions:
|
||||
ip_or_name:
|
||||
description:
|
||||
- Configures ntp server name or ip.
|
||||
required: true
|
||||
type: str
|
||||
enabled:
|
||||
description:
|
||||
- Disables/Enables ntp server
|
||||
type: bool
|
||||
trusted_enable:
|
||||
description:
|
||||
- Disables/Enables the trusted state for the ntp server.
|
||||
type: bool
|
||||
version:
|
||||
description:
|
||||
- version number for the ntp server
|
||||
choices: [3, 4]
|
||||
type: int
|
||||
key_id:
|
||||
description:
|
||||
- Used to configure the key-id for the ntp server
|
||||
type: int
|
||||
state:
|
||||
description:
|
||||
- Indicates if the ntp peer exists or should be deleted.
|
||||
choices: ['present', 'absent']
|
||||
type: str
|
||||
ntpdate:
|
||||
description:
|
||||
- Sets system clock once from a remote server using NTP.
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure NTP peers and servers
|
||||
onyx_ntp_peers_servers:
|
||||
peer:
|
||||
- ip_or_name: 1.1.1.1
|
||||
enabled: yes
|
||||
version: 4
|
||||
key_id: 6
|
||||
state: present
|
||||
server:
|
||||
- ip_or_name: 2.2.2.2
|
||||
enabled: true
|
||||
version: 3
|
||||
key_id: 8
|
||||
trusted_enable: no
|
||||
state: present
|
||||
ntpdate: 192.168.10.10
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device
|
||||
returned: always.
|
||||
type: list
|
||||
sample:
|
||||
- ntp peer 1.1.1.1 disable
|
||||
no ntp peer 1.1.1.1 disable
|
||||
ntp peer 1.1.1.1 keyId 6
|
||||
ntp peer 1.1.1.1 version 4
|
||||
no ntp peer 1.1.1.1
|
||||
ntp server 2.2.2.2 disable
|
||||
no ntp server 2.2.2.2 disable
|
||||
ntp server 2.2.2.2 keyID 8
|
||||
ntp server 2.2.2.2 version 3
|
||||
ntp server 2.2.2.2 trusted-enable
|
||||
no ntp server 2.2.2.2
|
||||
ntp server 192.168.10.10
|
||||
ntpdate 192.168.10.10
|
||||
"""
|
||||
|
||||
from copy import deepcopy
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxNTPServersPeersModule(BaseOnyxModule):
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
peer_spec = dict(ip_or_name=dict(required=True),
|
||||
enabled=dict(type='bool'),
|
||||
version=dict(type='int', choices=[3, 4]),
|
||||
key_id=dict(type='int'),
|
||||
state=dict(choices=['present', 'absent']))
|
||||
server_spec = dict(ip_or_name=dict(required=True),
|
||||
enabled=dict(type='bool'),
|
||||
version=dict(type='int', choices=[3, 4]),
|
||||
trusted_enable=dict(type='bool'),
|
||||
key_id=dict(type='int'),
|
||||
state=dict(choices=['present', 'absent']))
|
||||
element_spec = dict(peer=dict(type='list', elements='dict', options=peer_spec),
|
||||
server=dict(type='list', elements='dict', options=server_spec),
|
||||
ntpdate=dict())
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _show_peers_servers_config(self):
|
||||
cmd = "show ntp configured"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def _set_servers_config(self, peers_servers_config):
|
||||
servers = dict()
|
||||
peers = dict()
|
||||
if not peers_servers_config:
|
||||
return
|
||||
index = 0
|
||||
for peer_server in peers_servers_config:
|
||||
if (index == 0):
|
||||
index += 1
|
||||
continue
|
||||
else:
|
||||
header_list = peer_server.get("header").split(" ")
|
||||
header_type = header_list[1]
|
||||
if peer_server.get("Enabled") == "yes":
|
||||
enabled_state = True
|
||||
else:
|
||||
enabled_state = False
|
||||
if (header_type == 'server'):
|
||||
trusted_state = peer_server.get("Trusted")
|
||||
if trusted_state == 'yes':
|
||||
trusted_state = True
|
||||
else:
|
||||
trusted_state = False
|
||||
server_entry = {"version": peer_server.get("NTP version"),
|
||||
"enabled": enabled_state,
|
||||
"trusted_enable": trusted_state,
|
||||
"key_id": peer_server.get("Key ID")}
|
||||
servers[header_list[2]] = server_entry
|
||||
else:
|
||||
peer_entry = {"version": peer_server.get("NTP version"),
|
||||
"enabled": enabled_state,
|
||||
"key_id": peer_server.get("Key ID")}
|
||||
peers[header_list[2]] = peer_entry
|
||||
index += 1
|
||||
self._current_config = dict(server=servers,
|
||||
peer=peers)
|
||||
|
||||
def load_current_config(self):
|
||||
servers = dict()
|
||||
peers = dict()
|
||||
self._current_config = dict(server=servers,
|
||||
peer=peers)
|
||||
peers_servers_config = self._show_peers_servers_config()
|
||||
if peers_servers_config:
|
||||
self._set_servers_config(peers_servers_config)
|
||||
|
||||
def generate_commands(self):
|
||||
for option in self._current_config:
|
||||
req_ntp = self._required_config.get(option)
|
||||
if req_ntp is not None:
|
||||
for ntp_peer in req_ntp:
|
||||
peer_name = ntp_peer.get('ip_or_name')
|
||||
peer_key = ntp_peer.get('key_id')
|
||||
peer_state = ntp_peer.get("state")
|
||||
peer_enabled = ntp_peer.get("enabled")
|
||||
peer_version = ntp_peer.get("version")
|
||||
peer_key = ntp_peer.get("key_id")
|
||||
curr_name = self._current_config.get(option).get(peer_name)
|
||||
peer_version = ntp_peer.get('version')
|
||||
if self._current_config.get(option) and curr_name:
|
||||
if peer_state:
|
||||
if(peer_state == "absent"):
|
||||
self._commands.append('no ntp {0} {1}' .format(option, peer_name))
|
||||
continue
|
||||
if peer_enabled is not None:
|
||||
if curr_name.get("enabled") != peer_enabled:
|
||||
if(peer_enabled is True):
|
||||
self._commands.append('no ntp {0} {1} disable' .format(option, peer_name))
|
||||
else:
|
||||
self._commands.append('ntp {0} {1} disable' .format(option, peer_name))
|
||||
if peer_version:
|
||||
if (int(curr_name.get("version")) != peer_version):
|
||||
self._commands.append('ntp {0} {1} version {2}' .format(option, peer_name, peer_version))
|
||||
if peer_key:
|
||||
if curr_name.get("key_id") != "none":
|
||||
if (int(curr_name.get("key_id")) != peer_key):
|
||||
self._commands.append('ntp {0} {1} keyID {2}' .format(option, peer_name, peer_key))
|
||||
else:
|
||||
self._commands.append('ntp {0} {1} keyID {2}' .format(option, peer_name, peer_key))
|
||||
if option == "server":
|
||||
server_trusted = ntp_peer.get("trusted_enable")
|
||||
if server_trusted is not None:
|
||||
if (curr_name.get("trusted_enable") != server_trusted):
|
||||
if server_trusted is True:
|
||||
self._commands.append('ntp {0} {1} trusted-enable' .format(option, peer_name))
|
||||
else:
|
||||
self._commands.append('no ntp {0} {1} trusted-enable' .format(option, peer_name))
|
||||
else:
|
||||
if peer_state:
|
||||
if(peer_state == "absent"):
|
||||
continue
|
||||
if peer_enabled is not None:
|
||||
if(peer_enabled is True):
|
||||
self._commands.append('no ntp {0} {1} disable' .format(option, peer_name))
|
||||
else:
|
||||
self._commands.append('ntp {0} {1} disable' .format(option, peer_name))
|
||||
else:
|
||||
self._commands.append('ntp {0} {1} disable' .format(option, peer_name))
|
||||
if peer_version:
|
||||
self._commands.append('ntp {0} {1} version {2}' .format(option, peer_name, peer_version))
|
||||
if peer_key:
|
||||
self._commands.append('ntp {0} {1} keyID {2}' .format(option, peer_name, peer_key))
|
||||
|
||||
ntpdate = self._required_config.get("ntpdate")
|
||||
if ntpdate is not None:
|
||||
self._commands.append('ntpdate {0}' .format(ntpdate))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxNTPServersPeersModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
237
plugins/modules/network/onyx/onyx_ospf.py
Normal file
237
plugins/modules/network/onyx/onyx_ospf.py
Normal file
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_ospf
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Manage OSPF protocol on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management and configuration of OSPF
|
||||
protocol on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.4000
|
||||
options:
|
||||
ospf:
|
||||
description:
|
||||
- "OSPF instance number 1-65535"
|
||||
required: true
|
||||
router_id:
|
||||
description:
|
||||
- OSPF router ID. Required if I(state=present).
|
||||
interfaces:
|
||||
description:
|
||||
- List of interfaces and areas. Required if I(state=present).
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Interface name.
|
||||
required: true
|
||||
area:
|
||||
description:
|
||||
- OSPF area.
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- OSPF state.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: add ospf router to interface
|
||||
onyx_ospf:
|
||||
ospf: 2
|
||||
router_id: 192.168.8.2
|
||||
interfaces:
|
||||
- name: Eth1/1
|
||||
- area: 0.0.0.0
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- router ospf 2
|
||||
- router-id 192.168.8.2
|
||||
- exit
|
||||
- interface ethernet 1/1 ip ospf area 0.0.0.0
|
||||
"""
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxOspfModule(BaseOnyxModule):
|
||||
OSPF_IF_REGEX = re.compile(
|
||||
r'^(Loopback\d+|Eth\d+\/\d+|Vlan\d+|Po\d+)\s+(\S+).*')
|
||||
OSPF_ROUTER_REGEX = re.compile(r'^Routing Process (\d+).*ID\s+(\S+).*')
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
interface_spec = dict(
|
||||
name=dict(required=True),
|
||||
area=dict(required=True),
|
||||
)
|
||||
element_spec = dict(
|
||||
ospf=dict(type='int', required=True),
|
||||
router_id=dict(),
|
||||
interfaces=dict(type='list', elements='dict',
|
||||
options=interface_spec),
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
)
|
||||
return element_spec
|
||||
|
||||
def init_module(self):
|
||||
""" Ansible module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def validate_ospf(self, value):
|
||||
if value and not 1 <= int(value) <= 65535:
|
||||
self._module.fail_json(msg='ospf id must be between 1 and 65535')
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(
|
||||
ospf=module_params['ospf'],
|
||||
router_id=module_params['router_id'],
|
||||
state=module_params['state'],
|
||||
)
|
||||
interfaces = module_params['interfaces'] or list()
|
||||
req_interfaces = self._required_config['interfaces'] = dict()
|
||||
for interface_data in interfaces:
|
||||
req_interfaces[interface_data['name']] = interface_data['area']
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _update_ospf_data(self, ospf_data):
|
||||
match = self.OSPF_ROUTER_REGEX.match(ospf_data)
|
||||
if match:
|
||||
ospf_id = int(match.group(1))
|
||||
router_id = match.group(2)
|
||||
self._current_config['ospf'] = ospf_id
|
||||
self._current_config['router_id'] = router_id
|
||||
|
||||
def _update_ospf_interfaces(self, ospf_interfaces):
|
||||
interfaces = self._current_config['interfaces'] = dict()
|
||||
lines = ospf_interfaces.split('\n')
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
match = self.OSPF_IF_REGEX.match(line)
|
||||
if match:
|
||||
name = match.group(1)
|
||||
area = match.group(2)
|
||||
for prefix in ("Vlan", "Loopback"):
|
||||
if name.startswith(prefix):
|
||||
name = name.replace(prefix, prefix + ' ')
|
||||
interfaces[name] = area
|
||||
|
||||
def _get_ospf_config(self, ospf_id):
|
||||
cmd = 'show ip ospf %s | include Process' % ospf_id
|
||||
return show_cmd(self._module, cmd, json_fmt=False, fail_on_error=False)
|
||||
|
||||
def _get_ospf_interfaces_config(self, ospf_id):
|
||||
cmd = 'show ip ospf interface %s brief' % ospf_id
|
||||
return show_cmd(self._module, cmd, json_fmt=False, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
# called in base class in run function
|
||||
ospf_id = self._required_config['ospf']
|
||||
self._current_config = dict()
|
||||
ospf_data = self._get_ospf_config(ospf_id)
|
||||
if ospf_data:
|
||||
self._update_ospf_data(ospf_data)
|
||||
ospf_interfaces = self._get_ospf_interfaces_config(ospf_id)
|
||||
if ospf_interfaces:
|
||||
self._update_ospf_interfaces(ospf_interfaces)
|
||||
|
||||
def _generate_no_ospf_commands(self):
|
||||
req_ospf_id = self._required_config['ospf']
|
||||
curr_ospf_id = self._current_config.get('ospf')
|
||||
if curr_ospf_id == req_ospf_id:
|
||||
cmd = 'no router ospf %s' % req_ospf_id
|
||||
self._commands.append(cmd)
|
||||
|
||||
def _get_interface_command_name(self, if_name):
|
||||
if if_name.startswith('Eth'):
|
||||
return if_name.replace("Eth", "ethernet ")
|
||||
if if_name.startswith('Po'):
|
||||
return if_name.replace("Po", "port-channel ")
|
||||
if if_name.startswith('Vlan'):
|
||||
return if_name.replace("Vlan", "vlan")
|
||||
if if_name.startswith('Loopback'):
|
||||
return if_name.replace("Loopback", "loopback")
|
||||
self._module.fail_json(
|
||||
msg='invalid interface name: %s' % if_name)
|
||||
|
||||
def _get_interface_area_cmd(self, if_name, area):
|
||||
interface_prefix = self._get_interface_command_name(if_name)
|
||||
if area:
|
||||
area_cmd = 'ip ospf area %s' % area
|
||||
else:
|
||||
area_cmd = 'no ip ospf area'
|
||||
cmd = 'interface %s %s' % (interface_prefix, area_cmd)
|
||||
return cmd
|
||||
|
||||
def _generate_ospf_commands(self):
|
||||
req_router_id = self._required_config['router_id']
|
||||
req_ospf_id = self._required_config['ospf']
|
||||
curr_router_id = self._current_config.get('router_id')
|
||||
curr_ospf_id = self._current_config.get('ospf')
|
||||
if curr_ospf_id != req_ospf_id or req_router_id != curr_router_id:
|
||||
cmd = 'router ospf %s' % req_ospf_id
|
||||
self._commands.append(cmd)
|
||||
if req_router_id != curr_router_id:
|
||||
if req_router_id:
|
||||
cmd = 'router-id %s' % req_router_id
|
||||
else:
|
||||
cmd = 'no router-id'
|
||||
self._commands.append(cmd)
|
||||
self._commands.append('exit')
|
||||
req_interfaces = self._required_config['interfaces']
|
||||
curr_interfaces = self._current_config.get('interfaces', dict())
|
||||
for if_name, area in iteritems(req_interfaces):
|
||||
curr_area = curr_interfaces.get(if_name)
|
||||
if curr_area != area:
|
||||
cmd = self._get_interface_area_cmd(if_name, area)
|
||||
self._commands.append(cmd)
|
||||
for if_name in curr_interfaces:
|
||||
if if_name not in req_interfaces:
|
||||
cmd = self._get_interface_area_cmd(if_name, None)
|
||||
self._commands.append(cmd)
|
||||
|
||||
def generate_commands(self):
|
||||
req_state = self._required_config['state']
|
||||
if req_state == 'absent':
|
||||
return self._generate_no_ospf_commands()
|
||||
return self._generate_ospf_commands()
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxOspfModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
212
plugins/modules/network/onyx/onyx_pfc_interface.py
Normal file
212
plugins/modules/network/onyx/onyx_pfc_interface.py
Normal file
@@ -0,0 +1,212 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_pfc_interface
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Manage priority flow control on ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of priority flow control (PFC)
|
||||
on interfaces of Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.4000
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the interface PFC should be configured on.
|
||||
aggregate:
|
||||
description: List of interfaces PFC should be configured on.
|
||||
purge:
|
||||
description:
|
||||
- Purge interfaces not defined in the aggregate parameter.
|
||||
type: bool
|
||||
default: false
|
||||
state:
|
||||
description:
|
||||
- State of the PFC configuration.
|
||||
default: enabled
|
||||
choices: ['enabled', 'disabled']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure PFC
|
||||
onyx_pfc_interface:
|
||||
name: Eth1/1
|
||||
state: enabled
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- interface ethernet 1/17 dcb priority-flow-control mode on
|
||||
"""
|
||||
from copy import deepcopy
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxPfcInterfaceModule(BaseOnyxModule):
|
||||
PFC_IF_REGEX = re.compile(
|
||||
r"^(Eth\d+\/\d+)|(Eth\d+\/\d+\/\d+)|(Po\d+)|(Mpo\d+)$")
|
||||
|
||||
_purge = False
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
return dict(
|
||||
name=dict(type='str'),
|
||||
state=dict(default='enabled',
|
||||
choices=['enabled', 'disabled']),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_aggregate_spec(cls, element_spec):
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
return aggregate_spec
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
aggregate_spec = self._get_aggregate_spec(element_spec)
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict',
|
||||
options=aggregate_spec),
|
||||
purge=dict(default=False, type='bool'),
|
||||
)
|
||||
argument_spec.update(element_spec)
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate']]
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = list()
|
||||
module_params = self._module.params
|
||||
aggregate = module_params.get('aggregate')
|
||||
self._purge = module_params.get('purge', False)
|
||||
if aggregate:
|
||||
for item in aggregate:
|
||||
for key in item:
|
||||
if item.get(key) is None:
|
||||
item[key] = module_params[key]
|
||||
self.validate_param_values(item, item)
|
||||
req_item = item.copy()
|
||||
self._required_config.append(req_item)
|
||||
else:
|
||||
params = {
|
||||
'name': module_params['name'],
|
||||
'state': module_params['state'],
|
||||
}
|
||||
self.validate_param_values(params)
|
||||
self._required_config.append(params)
|
||||
|
||||
def _create_if_pfc_data(self, if_name, if_pfc_data):
|
||||
state = self.get_config_attr(if_pfc_data, "PFC oper")
|
||||
state = state.lower()
|
||||
return dict(
|
||||
name=if_name,
|
||||
state=state)
|
||||
|
||||
def _get_pfc_config(self):
|
||||
return show_cmd(self._module, "show dcb priority-flow-control")
|
||||
|
||||
def load_current_config(self):
|
||||
# called in base class in run function
|
||||
self._os_version = self._get_os_version()
|
||||
self._current_config = dict()
|
||||
pfc_config = self._get_pfc_config()
|
||||
if not pfc_config:
|
||||
return
|
||||
if self._os_version >= self.ONYX_API_VERSION:
|
||||
if len(pfc_config) >= 3:
|
||||
pfc_config = pfc_config[2]
|
||||
else:
|
||||
pfc_config = dict()
|
||||
else:
|
||||
if 'Table 2' in pfc_config:
|
||||
pfc_config = pfc_config['Table 2']
|
||||
|
||||
for if_name, if_pfc_data in iteritems(pfc_config):
|
||||
match = self.PFC_IF_REGEX.match(if_name)
|
||||
if not match:
|
||||
continue
|
||||
if if_pfc_data:
|
||||
if_pfc_data = if_pfc_data[0]
|
||||
self._current_config[if_name] = \
|
||||
self._create_if_pfc_data(if_name, if_pfc_data)
|
||||
|
||||
def _get_interface_cmd_name(self, if_name):
|
||||
if if_name.startswith('Eth'):
|
||||
return if_name.replace("Eth", "ethernet ")
|
||||
if if_name.startswith('Po'):
|
||||
return if_name.replace("Po", "port-channel ")
|
||||
if if_name.startswith('Mpo'):
|
||||
return if_name.replace("Mpo", "mlag-port-channel ")
|
||||
self._module.fail_json(
|
||||
msg='invalid interface name: %s' % if_name)
|
||||
|
||||
def _add_if_pfc_commands(self, if_name, req_state):
|
||||
cmd_prefix = "interface %s " % self._get_interface_cmd_name(if_name)
|
||||
|
||||
if req_state == 'disabled':
|
||||
pfc_cmd = 'no dcb priority-flow-control mode force'
|
||||
else:
|
||||
pfc_cmd = 'dcb priority-flow-control mode on force'
|
||||
self._commands.append(cmd_prefix + pfc_cmd)
|
||||
|
||||
def _gen_pfc_commands(self, if_name, curr_conf, req_state):
|
||||
curr_state = curr_conf.get('state', 'disabled')
|
||||
if curr_state != req_state:
|
||||
self._add_if_pfc_commands(if_name, req_state)
|
||||
|
||||
def generate_commands(self):
|
||||
req_interfaces = set()
|
||||
for req_conf in self._required_config:
|
||||
req_state = req_conf['state']
|
||||
if_name = req_conf['name']
|
||||
if req_state == 'enabled':
|
||||
req_interfaces.add(if_name)
|
||||
curr_conf = self._current_config.get(if_name, {})
|
||||
self._gen_pfc_commands(if_name, curr_conf, req_state)
|
||||
if self._purge:
|
||||
for if_name, curr_conf in iteritems(self._current_config):
|
||||
if if_name not in req_interfaces:
|
||||
req_state = 'disabled'
|
||||
self._gen_pfc_commands(if_name, curr_conf, req_state)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxPfcInterfaceModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
194
plugins/modules/network/onyx/onyx_protocol.py
Normal file
194
plugins/modules/network/onyx/onyx_protocol.py
Normal file
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_protocol
|
||||
author: "Samer Deeb (@samerd)"
|
||||
short_description: Enables/Disables protocols on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides a mechanism for enabling and disabling protocols
|
||||
Mellanox on ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.4000
|
||||
options:
|
||||
mlag:
|
||||
description: MLAG protocol
|
||||
choices: ['enabled', 'disabled']
|
||||
magp:
|
||||
description: MAGP protocol
|
||||
choices: ['enabled', 'disabled']
|
||||
spanning_tree:
|
||||
description: Spanning Tree support
|
||||
choices: ['enabled', 'disabled']
|
||||
dcb_pfc:
|
||||
description: DCB priority flow control
|
||||
choices: ['enabled', 'disabled']
|
||||
igmp_snooping:
|
||||
description: IP IGMP snooping
|
||||
choices: ['enabled', 'disabled']
|
||||
lacp:
|
||||
description: LACP protocol
|
||||
choices: ['enabled', 'disabled']
|
||||
ip_l3:
|
||||
description: IP L3 support
|
||||
choices: ['enabled', 'disabled']
|
||||
ip_routing:
|
||||
description: IP routing support
|
||||
choices: ['enabled', 'disabled']
|
||||
lldp:
|
||||
description: LLDP protocol
|
||||
choices: ['enabled', 'disabled']
|
||||
bgp:
|
||||
description: BGP protocol
|
||||
choices: ['enabled', 'disabled']
|
||||
ospf:
|
||||
description: OSPF protocol
|
||||
choices: ['enabled', 'disabled']
|
||||
nve:
|
||||
description: nve protocol
|
||||
choices: ['enabled', 'disabled']
|
||||
bfd:
|
||||
description: bfd protocol
|
||||
choices: ['enabled', 'disabled']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: enable protocols for MLAG
|
||||
onyx_protocol:
|
||||
lacp: enabled
|
||||
spanning_tree: disabled
|
||||
ip_routing: enabled
|
||||
mlag: enabled
|
||||
dcb_pfc: enabled
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- no spanning-tree
|
||||
- protocol mlag
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxProtocolModule(BaseOnyxModule):
|
||||
|
||||
PROTOCOL_MAPPING = dict(
|
||||
mlag=dict(name="mlag", enable="protocol mlag",
|
||||
disable="no protocol mlag"),
|
||||
magp=dict(name="magp", enable="protocol magp",
|
||||
disable="no protocol magp"),
|
||||
spanning_tree=dict(name="spanning-tree", enable="spanning-tree",
|
||||
disable="no spanning-tree"),
|
||||
dcb_pfc=dict(name="priority-flow-control",
|
||||
enable="dcb priority-flow-control enable force",
|
||||
disable="no dcb priority-flow-control enable force"),
|
||||
igmp_snooping=dict(name="igmp-snooping", enable="ip igmp snooping",
|
||||
disable="no ip igmp snooping"),
|
||||
lacp=dict(name="lacp", enable="lacp", disable="no lacp"),
|
||||
ip_l3=dict(name="IP L3", enable="ip l3",
|
||||
disable="no ip l3"),
|
||||
ip_routing=dict(name="IP routing", enable="ip routing",
|
||||
disable="no ip routing"),
|
||||
lldp=dict(name="lldp", enable="lldp", disable="no lldp"),
|
||||
bgp=dict(name="bgp", enable="protocol bgp", disable="no protocol bgp"),
|
||||
ospf=dict(name="ospf", enable="protocol ospf",
|
||||
disable="no protocol ospf"),
|
||||
nve=dict(name="nve", enable="protocol nve",
|
||||
disable="no protocol nve"),
|
||||
bfd=dict(name="bfd", enable="protocol bfd",
|
||||
disable="no protocol bfd"),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
element_spec = dict()
|
||||
for protocol in cls.PROTOCOL_MAPPING:
|
||||
element_spec[protocol] = dict(choices=['enabled', 'disabled'])
|
||||
return element_spec
|
||||
|
||||
def init_module(self):
|
||||
""" Ansible module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = dict()
|
||||
module_params = self._module.params
|
||||
for key, val in iteritems(module_params):
|
||||
if key in self.PROTOCOL_MAPPING and val is not None:
|
||||
self._required_config[key] = val
|
||||
|
||||
def _get_protocols(self):
|
||||
return show_cmd(self._module, "show protocols")
|
||||
|
||||
def _get_ip_routing(self):
|
||||
return show_cmd(self._module, 'show ip routing | include "IP routing"',
|
||||
json_fmt=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
protocols_config = self._get_protocols()
|
||||
if not protocols_config:
|
||||
protocols_config = dict()
|
||||
ip_config = self._get_ip_routing()
|
||||
if ip_config:
|
||||
lines = ip_config.split('\n')
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
line_attr = line.split(':')
|
||||
if len(line_attr) == 2:
|
||||
attr = line_attr[0].strip()
|
||||
val = line_attr[1].strip()
|
||||
protocols_config[attr] = val
|
||||
for protocol, protocol_metadata in iteritems(self.PROTOCOL_MAPPING):
|
||||
protocol_json_attr = protocol_metadata['name']
|
||||
val = protocols_config.get(protocol_json_attr, 'disabled')
|
||||
if val not in ('enabled', 'disabled'):
|
||||
val = 'enabled'
|
||||
self._current_config[protocol] = val
|
||||
|
||||
def generate_commands(self):
|
||||
for protocol, req_val in iteritems(self._required_config):
|
||||
protocol_metadata = self.PROTOCOL_MAPPING[protocol]
|
||||
curr_val = self._current_config.get(protocol, 'disabled')
|
||||
if curr_val != req_val:
|
||||
if req_val == 'disabled':
|
||||
command = protocol_metadata['disable']
|
||||
else:
|
||||
command = protocol_metadata['enable']
|
||||
self._commands.append(command)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxProtocolModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
206
plugins/modules/network/onyx/onyx_ptp_global.py
Normal file
206
plugins/modules/network/onyx/onyx_ptp_global.py
Normal file
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_ptp_global
|
||||
author: "Anas Badaha (@anasb)"
|
||||
short_description: Configures PTP Global parameters
|
||||
description:
|
||||
- This module provides declarative management of PTP Global configuration
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.8130
|
||||
ptp and ntp protocols cannot be enabled at the same time
|
||||
options:
|
||||
ptp_state:
|
||||
description:
|
||||
- PTP state.
|
||||
choices: ['enabled', 'disabled']
|
||||
default: enabled
|
||||
ntp_state:
|
||||
description:
|
||||
- NTP state.
|
||||
choices: ['enabled', 'disabled']
|
||||
domain:
|
||||
description:
|
||||
- "set PTP domain number Range 0-127"
|
||||
primary_priority:
|
||||
description:
|
||||
- "set PTP primary priority Range 0-225"
|
||||
secondary_priority:
|
||||
description:
|
||||
- "set PTP secondary priority Range 0-225"
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure PTP
|
||||
onyx_ptp_global:
|
||||
ntp_state: enabled
|
||||
ptp_state: disabled
|
||||
domain: 127
|
||||
primary_priority: 128
|
||||
secondary_priority: 128
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- no ntp enable
|
||||
- protocol ptp
|
||||
- ptp domain 127
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxPtpGlobalModule(BaseOnyxModule):
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
element_spec = dict(
|
||||
ntp_state=dict(choices=['enabled', 'disabled']),
|
||||
ptp_state=dict(choices=['enabled', 'disabled'], default='enabled'),
|
||||
domain=dict(type=int),
|
||||
primary_priority=dict(type=int),
|
||||
secondary_priority=dict(type=int)
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self._validate_param_values(self._required_config)
|
||||
|
||||
def _validate_param_values(self, obj, param=None):
|
||||
super(OnyxPtpGlobalModule, self).validate_param_values(obj, param)
|
||||
if obj['ntp_state'] == 'enabled' and obj['ptp_state'] == 'enabled':
|
||||
self._module.fail_json(msg='PTP State and NTP State Can not be enabled at the same time')
|
||||
|
||||
def validate_domain(self, value):
|
||||
if value and not 0 <= int(value) <= 127:
|
||||
self._module.fail_json(msg='domain must be between 0 and 127')
|
||||
|
||||
def validate_primary_priority(self, value):
|
||||
if value and not 0 <= int(value) <= 255:
|
||||
self._module.fail_json(msg='Primary Priority must be between 0 and 255')
|
||||
|
||||
def validate_secondary_priority(self, value):
|
||||
if value and not 0 <= int(value) <= 255:
|
||||
self._module.fail_json(msg='Secondary Priority must be between 0 and 255')
|
||||
|
||||
def _set_ntp_config(self, ntp_config):
|
||||
ntp_config = ntp_config[0]
|
||||
if not ntp_config:
|
||||
return
|
||||
ntp_state = ntp_config.get('NTP enabled')
|
||||
if ntp_state == "yes":
|
||||
self._current_config['ntp_state'] = "enabled"
|
||||
else:
|
||||
self._current_config['ntp_state'] = "disabled"
|
||||
|
||||
def _set_ptp_config(self, ptp_config):
|
||||
if ptp_config is None:
|
||||
self._current_config['ptp_state'] = 'disabled'
|
||||
else:
|
||||
self._current_config['ptp_state'] = 'enabled'
|
||||
self._current_config['domain'] = int(ptp_config['Domain'])
|
||||
self._current_config['primary_priority'] = int(ptp_config['Priority1'])
|
||||
self._current_config['secondary_priority'] = int(ptp_config['Priority2'])
|
||||
|
||||
def _show_ntp_config(self):
|
||||
cmd = "show ntp configured"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def _show_ptp_config(self):
|
||||
cmd = "show ptp clock"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
|
||||
ntp_config = self._show_ntp_config()
|
||||
self._set_ntp_config(ntp_config)
|
||||
|
||||
ptp_config = self._show_ptp_config()
|
||||
self._set_ptp_config(ptp_config)
|
||||
|
||||
def generate_commands(self):
|
||||
ntp_state = self._required_config.get("ntp_state")
|
||||
if ntp_state == "enabled":
|
||||
self._enable_ntp()
|
||||
elif ntp_state == "disabled":
|
||||
self._disable_ntp()
|
||||
|
||||
ptp_state = self._required_config.get("ptp_state", "enabled")
|
||||
if ptp_state == "enabled":
|
||||
self._enable_ptp()
|
||||
else:
|
||||
self._disable_ptp()
|
||||
|
||||
domain = self._required_config.get("domain")
|
||||
if domain is not None:
|
||||
curr_domain = self._current_config.get("domain")
|
||||
if domain != curr_domain:
|
||||
self._commands.append('ptp domain %d' % domain)
|
||||
|
||||
primary_priority = self._required_config.get("primary_priority")
|
||||
if primary_priority is not None:
|
||||
curr_primary_priority = self._current_config.get("primary_priority")
|
||||
if primary_priority != curr_primary_priority:
|
||||
self._commands.append('ptp priority1 %d' % primary_priority)
|
||||
|
||||
secondary_priority = self._required_config.get("secondary_priority")
|
||||
if secondary_priority is not None:
|
||||
curr_secondary_priority = self._current_config.get("secondary_priority")
|
||||
if secondary_priority != curr_secondary_priority:
|
||||
self._commands.append('ptp priority2 %d' % secondary_priority)
|
||||
|
||||
def _enable_ptp(self):
|
||||
curr_ptp_state = self._current_config['ptp_state']
|
||||
if curr_ptp_state == 'disabled':
|
||||
self._commands.append('protocol ptp')
|
||||
|
||||
def _disable_ptp(self):
|
||||
curr_ptp_state = self._current_config['ptp_state']
|
||||
if curr_ptp_state == 'enabled':
|
||||
self._commands.append('no protocol ptp')
|
||||
|
||||
def _enable_ntp(self):
|
||||
curr_ntp_state = self._current_config.get('ntp_state')
|
||||
if curr_ntp_state == 'disabled':
|
||||
self._commands.append('ntp enable')
|
||||
|
||||
def _disable_ntp(self):
|
||||
curr_ntp_state = self._current_config['ntp_state']
|
||||
if curr_ntp_state == 'enabled':
|
||||
self._commands.append('no ntp enable')
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxPtpGlobalModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
228
plugins/modules/network/onyx/onyx_ptp_interface.py
Normal file
228
plugins/modules/network/onyx/onyx_ptp_interface.py
Normal file
@@ -0,0 +1,228 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_ptp_interface
|
||||
author: 'Anas Badaha (@anasb)'
|
||||
short_description: 'Configures PTP on interface'
|
||||
description:
|
||||
- "This module provides declarative management of PTP interfaces configuration
|
||||
on Mellanox ONYX network devices."
|
||||
notes:
|
||||
- 'Tested on ONYX 3.6.8130'
|
||||
- 'PTP Protocol must be enabled on switch.'
|
||||
- 'Interface must not be a switch port interface.'
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- 'ethernet or vlan interface name that we want to configure PTP on it'
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- 'Enable/Disable PTP on Interface'
|
||||
default: enabled
|
||||
choices:
|
||||
- enabled
|
||||
- disabled
|
||||
delay_request:
|
||||
description:
|
||||
- 'configure PTP delay request interval, Range 0-5'
|
||||
announce_interval:
|
||||
description:
|
||||
- 'configure PTP announce setting for interval, Range -3-1'
|
||||
announce_timeout:
|
||||
description:
|
||||
- 'configure PTP announce setting for timeout, Range 2-10'
|
||||
sync_interval:
|
||||
description:
|
||||
- 'configure PTP sync interval, Range -7--1'
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure PTP interface
|
||||
onyx_ptp_interface:
|
||||
state: enabled
|
||||
name: Eth1/1
|
||||
delay_request: 0
|
||||
announce_interval: -2
|
||||
announce_timeout: 3
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- interface ethernet 1/16 ptp enable
|
||||
- interface ethernet 1/16 ptp delay-req interval 0
|
||||
- interface ethernet 1/16 ptp announce interval -1
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxPtpInterfaceModule(BaseOnyxModule):
|
||||
IF_ETH_REGEX = re.compile(r"^Eth(\d+\/\d+|Eth\d+\/\d+\d+)$")
|
||||
IF_VLAN_REGEX = re.compile(r"^Vlan (\d+)$")
|
||||
|
||||
IF_TYPE_ETH = "ethernet"
|
||||
IF_TYPE_VLAN = "vlan"
|
||||
|
||||
IF_TYPE_MAP = {
|
||||
IF_TYPE_ETH: IF_ETH_REGEX,
|
||||
IF_TYPE_VLAN: IF_VLAN_REGEX
|
||||
}
|
||||
|
||||
RANGE_ATTR = {
|
||||
"delay_request": (0, 5),
|
||||
"announce_interval": (-3, -1),
|
||||
"announce_timeout": (2, 10),
|
||||
"sync_interval": (-7, -1)
|
||||
}
|
||||
|
||||
_interface_type = None
|
||||
_interface_id = None
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
element_spec = dict(
|
||||
name=dict(required=True),
|
||||
state=dict(choices=['enabled', 'disabled'], default='enabled'),
|
||||
delay_request=dict(type=int),
|
||||
announce_interval=dict(type=int),
|
||||
announce_timeout=dict(type=int),
|
||||
sync_interval=dict(type=int)
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
@classmethod
|
||||
def _get_interface_type(cls, if_name):
|
||||
if_type = None
|
||||
if_id = None
|
||||
for interface_type, interface_regex in iteritems(cls.IF_TYPE_MAP):
|
||||
match = interface_regex.match(if_name)
|
||||
if match:
|
||||
if_type = interface_type
|
||||
if_id = match.group(1)
|
||||
break
|
||||
return if_type, if_id
|
||||
|
||||
def _set_if_type(self, module_params):
|
||||
if_name = module_params['name']
|
||||
self._interface_type, self._interface_id = self._get_interface_type(if_name)
|
||||
if not self._interface_id:
|
||||
self._module.fail_json(
|
||||
msg='unsupported interface name/type: %s' % if_name)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self._set_if_type(self._required_config)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _validate_attr_is_not_none(self, attr_name, attr_value):
|
||||
if attr_value is not None:
|
||||
self._module.fail_json(msg='Can not set %s value on switch while state is disabled' % attr_name)
|
||||
|
||||
def validate_param_values(self, obj, param=None):
|
||||
if obj['state'] == 'disabled':
|
||||
for attr_name in self.RANGE_ATTR:
|
||||
self._validate_attr_is_not_none(attr_name, obj[attr_name])
|
||||
super(OnyxPtpInterfaceModule, self).validate_param_values(obj, param)
|
||||
|
||||
def _validate_range(self, value, attr_name):
|
||||
min_value, max_value = self.RANGE_ATTR[attr_name]
|
||||
if value and not min_value <= int(value) <= max_value:
|
||||
self._module.fail_json(msg='%s value must be between %d and %d' % (attr_name, min_value, max_value))
|
||||
|
||||
def validate_delay_request(self, value):
|
||||
self._validate_range(value, "delay_request")
|
||||
|
||||
def validate_announce_interval(self, value):
|
||||
self._validate_range(value, "announce_interval")
|
||||
|
||||
def validate_announce_timeout(self, value):
|
||||
self._validate_range(value, "announce_timeout")
|
||||
|
||||
def validate_sync_interval(self, value):
|
||||
self._validate_range(value, "sync_interval")
|
||||
|
||||
def _set_ptp_interface_config(self, ptp_interface_config):
|
||||
if ptp_interface_config is None:
|
||||
self._current_config['state'] = 'disabled'
|
||||
return
|
||||
ptp_interface_config = ptp_interface_config[0]
|
||||
self._current_config['state'] = 'enabled'
|
||||
self._current_config['delay_request'] = int(ptp_interface_config['Delay request interval(log mean)'])
|
||||
self._current_config['announce_interval'] = int(ptp_interface_config['Announce interval(log mean)'])
|
||||
self._current_config['announce_timeout'] = int(ptp_interface_config['Announce receipt time out'])
|
||||
self._current_config['sync_interval'] = int(ptp_interface_config['Sync interval(log mean)'])
|
||||
|
||||
def _show_ptp_interface_config(self):
|
||||
cmd = "show ptp interface %s %s" % (self._interface_type, self._interface_id)
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
ptp_interface_config = self._show_ptp_interface_config()
|
||||
self._set_ptp_interface_config(ptp_interface_config)
|
||||
|
||||
def _generate_attr_command(self, attr_name, attr_cmd_name):
|
||||
attr_val = self._required_config.get(attr_name)
|
||||
if attr_val is not None:
|
||||
curr_val = self._current_config.get(attr_name)
|
||||
if attr_val != curr_val:
|
||||
self._commands.append(
|
||||
'interface %s %s ptp %s %d' % (self._interface_type, self._interface_id, attr_cmd_name, attr_val))
|
||||
|
||||
def generate_commands(self):
|
||||
state = self._required_config.get("state", "enabled")
|
||||
self._gen_ptp_commands(state)
|
||||
|
||||
self._generate_attr_command("delay_request", "delay-req interval")
|
||||
self._generate_attr_command("announce_interval", "announce interval")
|
||||
self._generate_attr_command("announce_timeout", "announce timeout")
|
||||
self._generate_attr_command("sync_interval", "sync interval")
|
||||
|
||||
def _add_if_ptp_cmd(self, req_state):
|
||||
if req_state == 'enabled':
|
||||
if_ptp_cmd = 'interface %s %s ptp enable' % (self._interface_type, self._interface_id)
|
||||
else:
|
||||
if_ptp_cmd = 'no interface %s %s ptp enable' % (self._interface_type, self._interface_id)
|
||||
self._commands.append(if_ptp_cmd)
|
||||
|
||||
def _gen_ptp_commands(self, req_state):
|
||||
curr_state = self._current_config.get('state')
|
||||
if curr_state != req_state:
|
||||
self._add_if_ptp_cmd(req_state)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxPtpInterfaceModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
235
plugins/modules/network/onyx/onyx_qos.py
Normal file
235
plugins/modules/network/onyx/onyx_qos.py
Normal file
@@ -0,0 +1,235 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_qos
|
||||
author: "Anas Badaha (@anasb)"
|
||||
short_description: Configures QoS
|
||||
description:
|
||||
- This module provides declarative management of Onyx QoS configuration
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX 3.6.8130
|
||||
options:
|
||||
interfaces:
|
||||
description:
|
||||
- list of interfaces name.
|
||||
required: true
|
||||
trust:
|
||||
description:
|
||||
- trust type.
|
||||
choices: ['L2', 'L3', 'both']
|
||||
default: L2
|
||||
rewrite_pcp:
|
||||
description:
|
||||
- rewrite with type pcp.
|
||||
choices: ['enabled', 'disabled']
|
||||
default: disabled
|
||||
rewrite_dscp:
|
||||
description:
|
||||
- rewrite with type dscp.
|
||||
choices: ['enabled', 'disabled']
|
||||
default: disabled
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure QoS
|
||||
onyx_QoS:
|
||||
interfaces:
|
||||
- Mpo7
|
||||
- Mpo7
|
||||
trust: L3
|
||||
rewrite_pcp: disabled
|
||||
rewrite_dscp: enabled
|
||||
|
||||
- name: configure QoS
|
||||
onyx_QoS:
|
||||
interfaces:
|
||||
- Eth1/1
|
||||
- Eth1/2
|
||||
trust: both
|
||||
rewrite_pcp: disabled
|
||||
rewrite_dscp: enabled
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- interface ethernet 1/16 qos trust L3
|
||||
- interface mlag-port-channel 7 qos trust L3
|
||||
- interface port-channel 1 qos trust L3
|
||||
- interface mlag-port-channel 7 qos trust L2
|
||||
- interface mlag-port-channel 7 qos rewrite dscp
|
||||
- interface ethernet 1/16 qos rewrite pcp
|
||||
- interface ethernet 1/1 no qos rewrite pcp
|
||||
"""
|
||||
|
||||
import re
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxQosModule(BaseOnyxModule):
|
||||
TRUST_CMD = "interface {0} {1} qos trust {2}"
|
||||
NO_REWRITE_PCP_CMD = "interface {0} {1} no qos rewrite pcp"
|
||||
NO_REWRITE_DSCP_CMD = "interface {0} {1} no qos rewrite dscp"
|
||||
REWRITE_PCP_CMD = "interface {0} {1} qos rewrite pcp"
|
||||
REWRITE_DSCP_CMD = "interface {0} {1} qos rewrite dscp"
|
||||
|
||||
REWRITE_PCP = "pcp"
|
||||
REWRITE_DSCP = "dscp"
|
||||
|
||||
IF_ETH_REGEX = re.compile(r"^Eth(\d+\/\d+|Eth\d+\/\d+\d+)$")
|
||||
IF_PO_REGEX = re.compile(r"^Po(\d+)$")
|
||||
MLAG_NAME_REGEX = re.compile(r"^Mpo(\d+)$")
|
||||
|
||||
IF_TYPE_ETH = "ethernet"
|
||||
PORT_CHANNEL = "port-channel"
|
||||
MLAG_PORT_CHANNEL = "mlag-port-channel"
|
||||
|
||||
IF_TYPE_MAP = {
|
||||
IF_TYPE_ETH: IF_ETH_REGEX,
|
||||
PORT_CHANNEL: IF_PO_REGEX,
|
||||
MLAG_PORT_CHANNEL: MLAG_NAME_REGEX
|
||||
}
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
element_spec = dict(
|
||||
interfaces=dict(type='list', required=True),
|
||||
trust=dict(choices=['L2', 'L3', 'both'], default='L2'),
|
||||
rewrite_pcp=dict(choices=['enabled', 'disabled'], default='disabled'),
|
||||
rewrite_dscp=dict(choices=['enabled', 'disabled'], default='disabled')
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _get_interface_type(self, if_name):
|
||||
if_type = None
|
||||
if_id = None
|
||||
for interface_type, interface_regex in iteritems(self.IF_TYPE_MAP):
|
||||
match = interface_regex.match(if_name)
|
||||
if match:
|
||||
if_type = interface_type
|
||||
if_id = match.group(1)
|
||||
break
|
||||
return if_type, if_id
|
||||
|
||||
def _set_interface_qos_config(self, interface_qos_config, interface, if_type, if_id):
|
||||
interface_qos_config = interface_qos_config[0].get(interface)
|
||||
trust = interface_qos_config[0].get("Trust mode")
|
||||
rewrite_dscp = interface_qos_config[0].get("DSCP rewrite")
|
||||
rewrite_pcp = interface_qos_config[0].get("PCP,DEI rewrite")
|
||||
|
||||
self._current_config[interface] = dict(trust=trust, rewrite_dscp=rewrite_dscp,
|
||||
rewrite_pcp=rewrite_pcp, if_type=if_type, if_id=if_id)
|
||||
|
||||
def _show_interface_qos(self, if_type, interface):
|
||||
cmd = "show qos interface {0} {1}".format(if_type, interface)
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
for interface in self._required_config.get("interfaces"):
|
||||
if_type, if_id = self._get_interface_type(interface)
|
||||
if not if_id:
|
||||
self._module.fail_json(
|
||||
msg='unsupported interface: {0}'.format(interface))
|
||||
interface_qos_config = self._show_interface_qos(if_type, if_id)
|
||||
if interface_qos_config is not None:
|
||||
self._set_interface_qos_config(interface_qos_config, interface, if_type, if_id)
|
||||
else:
|
||||
self._module.fail_json(
|
||||
msg='Interface {0} does not exist on switch'.format(interface))
|
||||
|
||||
def generate_commands(self):
|
||||
trust = self._required_config.get("trust")
|
||||
rewrite_pcp = self._required_config.get("rewrite_pcp")
|
||||
rewrite_dscp = self._required_config.get("rewrite_dscp")
|
||||
for interface in self._required_config.get("interfaces"):
|
||||
ignored1, ignored2, current_trust, if_type, if_id = self._get_current_rewrite_config(interface)
|
||||
self._add_interface_trust_cmds(if_type, if_id, interface, trust, current_trust)
|
||||
self._add_interface_rewrite_cmds(if_type, if_id, interface,
|
||||
rewrite_pcp, rewrite_dscp)
|
||||
|
||||
def _get_current_rewrite_config(self, interface):
|
||||
current_interface_qos_config = self._current_config.get(interface)
|
||||
current_rewrite_pcp = current_interface_qos_config.get('rewrite_pcp')
|
||||
current_rewrite_dscp = current_interface_qos_config.get('rewrite_dscp')
|
||||
if_type = current_interface_qos_config.get("if_type")
|
||||
if_id = current_interface_qos_config.get("if_id")
|
||||
current_trust = current_interface_qos_config.get('trust')
|
||||
|
||||
return current_rewrite_pcp, current_rewrite_dscp, current_trust, if_type, if_id
|
||||
|
||||
def _add_interface_trust_cmds(self, if_type, if_id, interface, trust, current_trust):
|
||||
|
||||
current_rewrite_pcp, current_rewrite_dscp, ignored1, ignored2, ignored3 = self._get_current_rewrite_config(
|
||||
interface)
|
||||
|
||||
if trust == "L3" and trust != current_trust:
|
||||
self._add_no_rewrite_cmd(if_type, if_id, interface, self.REWRITE_DSCP, current_rewrite_dscp)
|
||||
self._commands.append(self.TRUST_CMD.format(if_type, if_id, trust))
|
||||
elif trust == "L2" and trust != current_trust:
|
||||
self._add_no_rewrite_cmd(if_type, if_id, interface, self.REWRITE_PCP, current_rewrite_pcp)
|
||||
self._commands.append(self.TRUST_CMD.format(if_type, if_id, trust))
|
||||
elif trust == "both" and trust != current_trust:
|
||||
self._add_no_rewrite_cmd(if_type, if_id, interface, self.REWRITE_DSCP, current_rewrite_dscp)
|
||||
self._add_no_rewrite_cmd(if_type, if_id, interface, self.REWRITE_PCP, current_rewrite_pcp)
|
||||
self._commands.append(self.TRUST_CMD.format(if_type, if_id, trust))
|
||||
|
||||
def _add_interface_rewrite_cmds(self, if_type, if_id, interface, rewrite_pcp, rewrite_dscp):
|
||||
current_rewrite_pcp, current_rewrite_dscp, ignored1, ignored2, ignored3 = self._get_current_rewrite_config(
|
||||
interface)
|
||||
|
||||
if rewrite_pcp == "enabled" and rewrite_pcp != current_rewrite_pcp:
|
||||
self._commands.append(self.REWRITE_PCP_CMD.format(if_type, if_id))
|
||||
elif rewrite_pcp == "disabled" and rewrite_pcp != current_rewrite_pcp:
|
||||
self._commands.append(self.NO_REWRITE_PCP_CMD.format(if_type, if_id))
|
||||
|
||||
if rewrite_dscp == "enabled" and rewrite_dscp != current_rewrite_dscp:
|
||||
self._commands.append(self.REWRITE_DSCP_CMD.format(if_type, if_id))
|
||||
elif rewrite_dscp == "disabled" and rewrite_dscp != current_rewrite_dscp:
|
||||
self._commands.append(self.NO_REWRITE_DSCP_CMD.format(if_type, if_id))
|
||||
|
||||
def _add_no_rewrite_cmd(self, if_type, if_id, interface, rewrite_type, current_rewrite):
|
||||
if rewrite_type == self.REWRITE_PCP and current_rewrite == "enabled":
|
||||
self._commands.append(self.NO_REWRITE_PCP_CMD.format(if_type, if_id))
|
||||
self._current_config[interface]["rewrite_pcp"] = "disabled"
|
||||
elif rewrite_type == self.REWRITE_DSCP and current_rewrite == "enabled":
|
||||
self._commands.append(self.NO_REWRITE_DSCP_CMD.format(if_type, if_id))
|
||||
self._current_config[interface]["rewrite_dscp"] = "disabled"
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxQosModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
426
plugins/modules/network/onyx/onyx_snmp.py
Normal file
426
plugins/modules/network/onyx/onyx_snmp.py
Normal file
@@ -0,0 +1,426 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_snmp
|
||||
author: "Sara-Touqan (@sarato)"
|
||||
short_description: Manages SNMP general configurations on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of SNMP
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
state_enabled:
|
||||
description:
|
||||
- Enables/Disables the state of the SNMP configuration.
|
||||
type: bool
|
||||
contact_name:
|
||||
description:
|
||||
- Sets the SNMP contact name.
|
||||
type: str
|
||||
location:
|
||||
description:
|
||||
- Sets the SNMP location.
|
||||
type: str
|
||||
communities_enabled:
|
||||
description:
|
||||
- Enables/Disables community-based authentication on the system.
|
||||
type: bool
|
||||
multi_communities_enabled:
|
||||
description:
|
||||
- Enables/Disables multiple communities to be configured.
|
||||
type: bool
|
||||
snmp_communities:
|
||||
type: list
|
||||
description:
|
||||
- List of snmp communities
|
||||
suboptions:
|
||||
community_name:
|
||||
description:
|
||||
- Configures snmp community name.
|
||||
required: true
|
||||
type: str
|
||||
community_type:
|
||||
description:
|
||||
- Add this community as either a read-only or read-write community.
|
||||
choices: ['read-only', 'read-write']
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Used to decide if you want to delete the given snmp community or not
|
||||
choices: ['present', 'absent']
|
||||
type: str
|
||||
notify_enabled:
|
||||
description:
|
||||
- Enables/Disables sending of SNMP notifications (traps and informs) from thee system.
|
||||
type: bool
|
||||
notify_port:
|
||||
description:
|
||||
- Sets the default port to which notifications are sent.
|
||||
type: str
|
||||
notify_community:
|
||||
description:
|
||||
- Sets the default community for SNMP v1 and v2c notifications sent to hosts which do not have a community override set.
|
||||
type: str
|
||||
notify_send_test:
|
||||
description:
|
||||
- Sends a test notification.
|
||||
type: str
|
||||
choices: ['yes','no']
|
||||
notify_event:
|
||||
description:
|
||||
- Specifys which events will be sent as SNMP notifications.
|
||||
type: str
|
||||
choices: ['asic-chip-down', 'dcbx-pfc-port-oper-state-trap', 'insufficient-power', 'mstp-new-bridge-root',
|
||||
'ospf-lsdb-approaching-overflow', 'sm-stop', 'user-logout', 'cli-line-executed', 'dcbx-pfc-port-peer-state-trap',
|
||||
'interface-down', 'mstp-new-root-port', 'ospf-lsdb-overflow', 'snmp-authtrap', 'xstp-new-root-bridge',
|
||||
'cpu-util-high', 'disk-io-high', 'interface-up', 'mstp-topology-change', 'ospf-nbr-state-change',
|
||||
'temperature-too-high', 'xstp-root-port-change', 'dcbx-ets-module-state-change', 'disk-space-low',
|
||||
'internal-bus-error', 'netusage-high', 'paging-high', 'topology_change', 'xstp-topology-change',
|
||||
'dcbx-ets-port-admin-state-trap', 'entity-state-change', 'internal-link-speed-mismatch', 'new_root',
|
||||
'power-redundancy-mismatch', 'unexpected-cluster-join', 'dcbx-ets-port-oper-state-trap', 'expected-shutdown',
|
||||
'liveness-failure', 'ospf-auth-fail', 'process-crash', 'unexpected-cluster-leave', 'dcbx-ets-port-peer-state-trap',
|
||||
'health-module-status', 'low-power', 'ospf-config-error', 'process-exit', 'unexpected-cluster-size',
|
||||
'dcbx-pfc-module-state-change', 'insufficient-fans', 'low-power-recover', 'ospf-if-rx-bad-packet',
|
||||
'sm-restart', 'unexpected-shutdown', 'dcbx-pfc-port-admin-state-trap', 'insufficient-fans-recover', 'memusage-high',
|
||||
'ospf-if-state-change', 'sm-start', 'user-login']
|
||||
engine_id_reset:
|
||||
description:
|
||||
- Sets SNMPv3 engineID to node unique value.
|
||||
type: bool
|
||||
snmp_permissions:
|
||||
type: list
|
||||
description:
|
||||
- Allow SNMPSET requests for items in a MIB.
|
||||
suboptions:
|
||||
state_enabled:
|
||||
description:
|
||||
- Enables/Disables the request.
|
||||
required: true
|
||||
type: bool
|
||||
permission_type:
|
||||
description:
|
||||
- Configures the request type.
|
||||
choices: ['MELLANOX-CONFIG-DB-MIB', 'MELLANOX-EFM-MIB','MELLANOX-POWER-CYCLE','MELLANOX-SW-UPDATE','RFC1213-MIB']
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure SNMP
|
||||
onyx_snmp:
|
||||
state_enabled: yes
|
||||
contact_name: sara
|
||||
location: Nablus
|
||||
communities_enabled: no
|
||||
multi_communities_enabled: no
|
||||
notify_enabled: yes
|
||||
notify_port: 1
|
||||
notify_community: community_1
|
||||
notify_send_test: yes
|
||||
notify_event: temperature-too-high
|
||||
snmp_communities:
|
||||
- community_name: public
|
||||
community_type: read-only
|
||||
state: absent
|
||||
snmp_permissions:
|
||||
- state_enabled: yes
|
||||
permission_type: MELLANOX-CONFIG-DB-MIB
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device
|
||||
returned: always.
|
||||
type: list
|
||||
sample:
|
||||
- snmp-server enable
|
||||
- no snmp-server enable
|
||||
- snmp-server location <location_name>
|
||||
- snmp-server contact <contact_name>
|
||||
- snmp-server enable communities
|
||||
- no snmp-server enable communities
|
||||
- snmp-server enable mult-communities
|
||||
- no snmp-server enable mult-communities
|
||||
- snmp-server enable notify
|
||||
- snmp-server notify port <port_number>
|
||||
- snmp-server notify community <community_name>
|
||||
- snmp-server notify send-test
|
||||
- snmp-server notify event <event_name>
|
||||
- snmp-server enable set-permission <permission_type>
|
||||
- no snmp-server enable set-permission <permission_type>
|
||||
- snmp-server community <community_name> <community_type>
|
||||
- no snmp-server community <community_name>.
|
||||
- snmp-server engineID reset.
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxSNMPModule(BaseOnyxModule):
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
|
||||
community_spec = dict(community_name=dict(required=True),
|
||||
community_type=dict(choices=['read-only', 'read-write']),
|
||||
state=dict(choices=['present', 'absent']))
|
||||
|
||||
snmp_permission_spec = dict(state_enabled=dict(type='bool', required=True),
|
||||
permission_type=dict(choices=['MELLANOX-CONFIG-DB-MIB', 'MELLANOX-EFM-MIB', 'MELLANOX-POWER-CYCLE',
|
||||
'MELLANOX-SW-UPDATE', 'RFC1213-MIB']))
|
||||
|
||||
event_choices = ['asic-chip-down', 'dcbx-pfc-port-oper-state-trap', 'insufficient-power', 'mstp-new-bridge-root',
|
||||
'ospf-lsdb-approaching-overflow', 'sm-stop', 'user-logout', 'cli-line-executed', 'dcbx-pfc-port-peer-state-trap',
|
||||
'interface-down', 'mstp-new-root-port', 'ospf-lsdb-overflow', 'snmp-authtrap', 'xstp-new-root-bridge',
|
||||
'cpu-util-high', 'disk-io-high', 'interface-up', 'mstp-topology-change', 'ospf-nbr-state-change',
|
||||
'temperature-too-high', 'xstp-root-port-change', 'dcbx-ets-module-state-change', 'disk-space-low',
|
||||
'internal-bus-error', 'netusage-high', 'paging-high', 'topology_change', 'xstp-topology-change',
|
||||
'dcbx-ets-port-admin-state-trap', 'entity-state-change', 'internal-link-speed-mismatch', 'new_root',
|
||||
'power-redundancy-mismatch', 'unexpected-cluster-join', 'dcbx-ets-port-oper-state-trap', 'expected-shutdown',
|
||||
'liveness-failure', 'ospf-auth-fail', 'process-crash', 'unexpected-cluster-leave', 'dcbx-ets-port-peer-state-trap',
|
||||
'health-module-status', 'low-power', 'ospf-config-error', 'process-exit', 'unexpected-cluster-size',
|
||||
'dcbx-pfc-module-state-change', 'insufficient-fans', 'low-power-recover', 'ospf-if-rx-bad-packet',
|
||||
'sm-restart', 'unexpected-shutdown', 'dcbx-pfc-port-admin-state-trap', 'insufficient-fans-recover', 'memusage-high',
|
||||
'ospf-if-state-change', 'sm-start', 'user-login']
|
||||
element_spec = dict(
|
||||
state_enabled=dict(type='bool'),
|
||||
contact_name=dict(type='str'),
|
||||
location=dict(type='str'),
|
||||
communities_enabled=dict(type='bool'),
|
||||
multi_communities_enabled=dict(type='bool'),
|
||||
snmp_communities=dict(type='list', elements='dict', options=community_spec),
|
||||
notify_enabled=dict(type='bool'),
|
||||
notify_port=dict(type='str'),
|
||||
notify_community=dict(type='str'),
|
||||
notify_send_test=dict(type='str', choices=['yes', 'no']),
|
||||
notify_event=dict(type='str', choices=event_choices),
|
||||
engine_id_reset=dict(type='bool'),
|
||||
snmp_permissions=dict(type='list', elements='dict', options=snmp_permission_spec)
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _show_snmp_config(self):
|
||||
show_cmds = []
|
||||
cmd = "show snmp"
|
||||
show_cmds.append(show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False))
|
||||
cmd = "show running-config | include snmp"
|
||||
show_cmds.append(show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False))
|
||||
return show_cmds
|
||||
|
||||
def _set_snmp_config(self, all_snmp_config):
|
||||
ro_communities_list = []
|
||||
rw_communities_list = []
|
||||
snmp_config = all_snmp_config[0]
|
||||
if not snmp_config:
|
||||
return
|
||||
if snmp_config.get("SNMP enabled") == 'yes':
|
||||
self._current_config['state_enabled'] = True
|
||||
else:
|
||||
self._current_config['state_enabled'] = False
|
||||
self._current_config['contact_name'] = snmp_config.get("System contact")
|
||||
self._current_config['location'] = snmp_config.get("System location")
|
||||
curr_ro_comm = snmp_config.get("Read-only community")
|
||||
if curr_ro_comm:
|
||||
ro_arr = curr_ro_comm.split(' ')
|
||||
rw_arr = snmp_config.get("Read-write community").split(' ')
|
||||
ro_communities_list = ro_arr[0]
|
||||
rw_communities_list = rw_arr[0]
|
||||
if (len(ro_arr) == 2):
|
||||
self._current_config['communities_enabled'] = False
|
||||
else:
|
||||
self._current_config['communities_enabled'] = True
|
||||
else:
|
||||
read_only_communities = all_snmp_config[1]
|
||||
read_write_communities = all_snmp_config[2]
|
||||
if not read_only_communities:
|
||||
return
|
||||
read_only_comm = read_only_communities.get("Read-only communities")
|
||||
if read_only_comm:
|
||||
self._current_config['communities_enabled'] = True
|
||||
ro_communities_list = read_only_comm[0].get("Lines")
|
||||
else:
|
||||
self._current_config['communities_enabled'] = False
|
||||
ro_comm_disabled = read_only_communities.get("Read-only communities (DISABLED)")
|
||||
if ro_comm_disabled:
|
||||
ro_communities_list = ro_comm_disabled[0].get("Lines")
|
||||
if not read_write_communities:
|
||||
return
|
||||
read_write_comm = read_write_communities.get("Read-write communities")
|
||||
if read_write_comm:
|
||||
self._current_config['communities_enabled'] = True
|
||||
rw_communities_list = read_write_comm[0].get("Lines")
|
||||
else:
|
||||
self._current_config['communities_enabled'] = False
|
||||
rw_comm_disabled = read_write_communities.get("Read-write communities (DISABLED)")
|
||||
if rw_comm_disabled:
|
||||
rw_communities_list = rw_comm_disabled[0].get("Lines")
|
||||
self._current_config['ro_communities_list'] = ro_communities_list
|
||||
self._current_config['rw_communities_list'] = rw_communities_list
|
||||
|
||||
def _set_snmp_running_config(self, snmp_running_config):
|
||||
self._current_config['multi_comm_enabled'] = True
|
||||
self._current_config['notify_enabled'] = True
|
||||
curr_config_arr = []
|
||||
snmp_lines = snmp_running_config.get('Lines')
|
||||
for runn_config in snmp_lines:
|
||||
curr_config_arr.append(runn_config.strip())
|
||||
if 'no snmp-server enable mult-communities' in snmp_lines:
|
||||
self._current_config['multi_comm_enabled'] = False
|
||||
if 'no snmp-server enable notify' in snmp_lines:
|
||||
self._current_config['notify_enabled'] = False
|
||||
self._current_config['snmp_running_config'] = curr_config_arr
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
snmp_config = self._show_snmp_config()
|
||||
if snmp_config[0]:
|
||||
self._set_snmp_config(snmp_config[0])
|
||||
if snmp_config[1]:
|
||||
self._set_snmp_running_config(snmp_config[1])
|
||||
|
||||
def generate_commands(self):
|
||||
current_state = self._current_config.get("state_enabled")
|
||||
state = current_state
|
||||
req_state = self._required_config.get("state_enabled")
|
||||
if req_state is not None:
|
||||
state = req_state
|
||||
if state is not None:
|
||||
if current_state != state:
|
||||
if state is True:
|
||||
self._commands.append('snmp-server enable')
|
||||
else:
|
||||
self._commands.append('no snmp-server enable')
|
||||
|
||||
contact_name = self._required_config.get("contact_name")
|
||||
if contact_name:
|
||||
current_contact_name = self._current_config.get("contact_name")
|
||||
if contact_name is not None:
|
||||
if current_contact_name != contact_name:
|
||||
self._commands.append('snmp-server contact {0}' .format(contact_name))
|
||||
|
||||
location = self._required_config.get("location")
|
||||
if location:
|
||||
current_location = self._current_config.get("location")
|
||||
if location is not None:
|
||||
if current_location != location:
|
||||
self._commands.append('snmp-server location {0}' .format(location))
|
||||
|
||||
communities_enabled = self._required_config.get("communities_enabled")
|
||||
if communities_enabled is not None:
|
||||
current_communities_enabled = self._current_config.get("communities_enabled")
|
||||
if communities_enabled is not None:
|
||||
if current_communities_enabled != communities_enabled:
|
||||
if communities_enabled is True:
|
||||
self._commands.append('snmp-server enable communities')
|
||||
else:
|
||||
self._commands.append('no snmp-server enable communities')
|
||||
|
||||
ro_communities = self._current_config.get("ro_communities_list")
|
||||
rw_communities = self._current_config.get("rw_communities_list")
|
||||
snmp_communities = self._required_config.get("snmp_communities")
|
||||
if snmp_communities:
|
||||
if snmp_communities is not None:
|
||||
for community in snmp_communities:
|
||||
community_name = community.get("community_name")
|
||||
state = community.get("state")
|
||||
if state:
|
||||
if state == 'absent':
|
||||
self._commands.append('no snmp-server community {0}' .format(community_name))
|
||||
continue
|
||||
community_type = community.get("community_type")
|
||||
if community_type:
|
||||
if community_type == 'read-only':
|
||||
if community_name not in ro_communities:
|
||||
self._commands.append('snmp-server community {0} ro' .format(community_name))
|
||||
else:
|
||||
if community_name not in rw_communities:
|
||||
self._commands.append('snmp-server community {0} rw' .format(community_name))
|
||||
else:
|
||||
if community_name not in ro_communities:
|
||||
self._commands.append('snmp-server community {0}' .format(community_name))
|
||||
|
||||
engine_id_reset = self._required_config.get("engine_id_reset")
|
||||
if engine_id_reset is not None:
|
||||
if engine_id_reset:
|
||||
self._commands.append('snmp-server engineID reset')
|
||||
|
||||
current_multi_comm_state = self._current_config.get("multi_comm_enabled")
|
||||
multi_communities_enabled = self._required_config.get("multi_communities_enabled")
|
||||
if multi_communities_enabled is not None:
|
||||
if current_multi_comm_state != multi_communities_enabled:
|
||||
if multi_communities_enabled is True:
|
||||
self._commands.append('snmp-server enable mult-communities')
|
||||
else:
|
||||
self._commands.append('no snmp-server enable mult-communities')
|
||||
|
||||
notify_enabled = self._required_config.get("notify_enabled")
|
||||
if notify_enabled is not None:
|
||||
current_notify_state = self._current_config.get("notify_enabled")
|
||||
if current_notify_state != notify_enabled:
|
||||
if notify_enabled is True:
|
||||
self._commands.append('snmp-server enable notify')
|
||||
else:
|
||||
self._commands.append('no snmp-server enable notify')
|
||||
|
||||
snmp_permissions = self._required_config.get("snmp_permissions")
|
||||
if snmp_permissions is not None:
|
||||
for permission in snmp_permissions:
|
||||
permission_type = permission.get('permission_type')
|
||||
if permission.get('state_enabled') is True:
|
||||
self._commands.append('snmp-server enable set-permission {0}' .format(permission_type))
|
||||
else:
|
||||
self._commands.append('no snmp-server enable set-permission {0}' .format(permission_type))
|
||||
|
||||
snmp_running_config = self._current_config.get("snmp_running_config")
|
||||
notify_port = self._required_config.get("notify_port")
|
||||
if notify_port is not None:
|
||||
notified_cmd = 'snmp-server notify port {0}' .format(notify_port)
|
||||
if notified_cmd not in snmp_running_config:
|
||||
self._commands.append('snmp-server notify port {0}' .format(notify_port))
|
||||
|
||||
notify_community = self._required_config.get("notify_community")
|
||||
if notify_community is not None:
|
||||
notified_cmd = 'snmp-server notify community {0}' .format(notify_community)
|
||||
if notified_cmd not in snmp_running_config:
|
||||
self._commands.append('snmp-server notify community {0}' .format(notify_community))
|
||||
|
||||
notify_send_test = self._required_config.get("notify_send_test")
|
||||
if notify_send_test is not None:
|
||||
if notify_send_test == 'yes':
|
||||
self._commands.append('snmp-server notify send-test')
|
||||
|
||||
notify_event = self._required_config.get("notify_event")
|
||||
if notify_event is not None:
|
||||
self._commands.append('snmp-server notify event {0}' .format(notify_event))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxSNMPModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
424
plugins/modules/network/onyx/onyx_snmp_hosts.py
Normal file
424
plugins/modules/network/onyx/onyx_snmp_hosts.py
Normal file
@@ -0,0 +1,424 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_snmp_hosts
|
||||
author: "Sara Touqan (@sarato)"
|
||||
short_description: Configures SNMP host parameters
|
||||
description:
|
||||
- This module provides declarative management of SNMP hosts protocol params
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
hosts:
|
||||
type: list
|
||||
description:
|
||||
- List of snmp hosts
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Specifies the name of the host.
|
||||
required: true
|
||||
type: str
|
||||
enabled:
|
||||
description:
|
||||
- Temporarily Enables/Disables sending of all notifications to this host.
|
||||
type: bool
|
||||
notification_type:
|
||||
description:
|
||||
- Configures the type of sending notification to the specified host.
|
||||
choices: ['trap', 'inform']
|
||||
type: str
|
||||
port:
|
||||
description:
|
||||
- Overrides default target port for this host.
|
||||
type: str
|
||||
version:
|
||||
description:
|
||||
- Specifys SNMP version of informs to send.
|
||||
choices: ['1', '2c', '3']
|
||||
type: str
|
||||
user_name:
|
||||
description:
|
||||
- Specifys username for this inform sink.
|
||||
type: str
|
||||
auth_type:
|
||||
description:
|
||||
- Configures SNMP v3 security parameters, specifying passwords in a nother parameter (auth_password) (passwords are always stored encrypted).
|
||||
choices: ['md5', 'sha', 'sha224', 'sha256', 'sha384', 'sha512']
|
||||
type: str
|
||||
auth_password:
|
||||
description:
|
||||
- The password needed to configure the auth type.
|
||||
type: str
|
||||
privacy_type:
|
||||
description:
|
||||
- Specifys SNMP v3 privacy settings for this user.
|
||||
choices: ['3des', 'aes-128', 'aes-192', 'aes-192-cfb', 'aes-256', 'aes-256-cfb', 'des']
|
||||
type: str
|
||||
privacy_password:
|
||||
description:
|
||||
- The password needed to configure the privacy type.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Used to decide if you want to delete the specified host or not.
|
||||
choices: ['present' , 'absent']
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: enables snmp host
|
||||
onyx_snmp_hosts:
|
||||
hosts:
|
||||
- name: 1.1.1.1
|
||||
enabled: true
|
||||
|
||||
- name: configures snmp host with version 2c
|
||||
onyx_snmp_hosts:
|
||||
hosts:
|
||||
- name: 2.3.2.4
|
||||
enabled: true
|
||||
notification_type: trap
|
||||
port: 66
|
||||
version: 2c
|
||||
|
||||
- name: configures snmp host with version 3 and configures it with user as sara
|
||||
onyx_snmp_hosts:
|
||||
hosts:
|
||||
- name: 2.3.2.4
|
||||
enabled: true
|
||||
notification_type: trap
|
||||
port: 66
|
||||
version: 3
|
||||
user_name: sara
|
||||
auth_type: sha
|
||||
auth_password: jnbdfijbdsf
|
||||
privacy_type: 3des
|
||||
privacy_password: nojfd8uherwiugfh
|
||||
|
||||
- name: deletes the snmp host
|
||||
onyx_snmp_hosts:
|
||||
hosts:
|
||||
- name: 2.3.2.4
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- snmp-server host <host_name> disable
|
||||
- no snmp-server host <host_name> disable
|
||||
- snmp-server host <host_name> informs port <port_number> version <version_number>
|
||||
- snmp-server host <host_name> traps port <port_number> version <version_number>
|
||||
- snmp-server host <host_name> informs port <port_number> version <version_number> user <user_name> auth <auth_type>
|
||||
<auth_password> priv <privacy_type> <privacy_password>
|
||||
- snmp-server host <host_name> traps port <port_number> version <version_number> user <user_name> auth <auth_type>
|
||||
<auth_password> priv <privacy_type> <privacy_password>
|
||||
- no snmp-server host <host_name>.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxSNMPHostsModule(BaseOnyxModule):
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
host_spec = dict(name=dict(required=True),
|
||||
enabled=dict(type='bool'),
|
||||
notification_type=dict(type='str', choices=['trap', 'inform']),
|
||||
port=dict(type='str'),
|
||||
version=dict(type='str', choices=['1', '2c', '3']),
|
||||
user_name=dict(type='str'),
|
||||
auth_type=dict(type='str', choices=['md5', 'sha', 'sha224', 'sha256', 'sha384', 'sha512']),
|
||||
privacy_type=dict(type='str', choices=['3des', 'aes-128', 'aes-192', 'aes-192-cfb', 'aes-256', 'aes-256-cfb', 'des']),
|
||||
privacy_password=dict(type='str', no_log=True),
|
||||
auth_password=dict(type='str', no_log=True),
|
||||
state=dict(type='str', choices=['present', 'absent'])
|
||||
)
|
||||
element_spec = dict(
|
||||
hosts=dict(type='list', elements='dict', options=host_spec),
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def validate_snmp_required_params(self):
|
||||
req_hosts = self._required_config.get("hosts")
|
||||
if req_hosts:
|
||||
for host in req_hosts:
|
||||
version = host.get('version')
|
||||
if version:
|
||||
if version == '3':
|
||||
if host.get('user_name') is None or host.get('auth_type') is None or host.get('auth_password') is None:
|
||||
self._module.fail_json(msg='user_name, auth_type and auth_password are required when version number is 3.')
|
||||
|
||||
if host.get('notification_type') is not None:
|
||||
if host.get('version') is None or host.get('port') is None:
|
||||
self._module.fail_json(msg='port and version are required when notification_type is provided.')
|
||||
|
||||
if host.get('auth_type') is not None:
|
||||
if host.get('auth_password') is None:
|
||||
self._module.fail_json(msg='auth_password is required when auth_type is provided.')
|
||||
|
||||
if host.get('privacy_type') is not None:
|
||||
if host.get('privacy_password') is None:
|
||||
self._module.fail_json(msg='privacy_password is required when privacy_type is provided.')
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
self.validate_snmp_required_params()
|
||||
|
||||
def _set_host_config(self, hosts_config):
|
||||
hosts = hosts_config.get('Notification sinks')
|
||||
if hosts[0].get('Lines'):
|
||||
self._current_config['current_hosts'] = dict()
|
||||
self._current_config['host_names'] = []
|
||||
return
|
||||
|
||||
current_hosts = dict()
|
||||
host_names = []
|
||||
for host in hosts:
|
||||
host_info = dict()
|
||||
for host_name in host:
|
||||
host_names.append(host_name)
|
||||
enabled = True
|
||||
first_entry = host.get(host_name)[0]
|
||||
if first_entry:
|
||||
if first_entry.get('Enabled') == 'no':
|
||||
enabled = False
|
||||
notification_type = first_entry.get('Notification type')
|
||||
notification_type = notification_type.split()
|
||||
host_info['notification_type'] = notification_type[2]
|
||||
version = notification_type[1][1:]
|
||||
host_info['port'] = first_entry.get('Port')
|
||||
host_info['name'] = host_name
|
||||
host_info['enabled'] = enabled
|
||||
host_info['version'] = version
|
||||
if first_entry.get('Community') is None:
|
||||
if len(first_entry) == 8:
|
||||
host_info['user_name'] = first_entry.get('Username')
|
||||
host_info['auth_type'] = first_entry.get('Authentication type')
|
||||
host_info['privacy_type'] = first_entry.get('Privacy type')
|
||||
elif len(host.get(host_name)) == 2:
|
||||
second_entry = host.get(host_name)[1]
|
||||
host_info['user_name'] = second_entry.get('Username')
|
||||
host_info['auth_type'] = second_entry.get('Authentication type')
|
||||
host_info['privacy_type'] = second_entry.get('Privacy type')
|
||||
else:
|
||||
host_info['user_name'] = ''
|
||||
host_info['auth_type'] = ''
|
||||
host_info['privacy_type'] = ''
|
||||
else:
|
||||
host_info['user_name'] = ''
|
||||
host_info['auth_type'] = ''
|
||||
host_info['privacy_type'] = ''
|
||||
current_hosts[host_name] = host_info
|
||||
self._current_config['current_hosts'] = current_hosts
|
||||
self._current_config['host_names'] = host_names
|
||||
|
||||
def _show_hosts_config(self):
|
||||
cmd = "show snmp host"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
hosts_config = self._show_hosts_config()
|
||||
if hosts_config[1]:
|
||||
self._set_host_config(hosts_config[1])
|
||||
|
||||
def generate_snmp_commands_with_current_config(self, host):
|
||||
host_id = host.get('name')
|
||||
host_notification_type = host.get('notification_type')
|
||||
host_enabled = host.get("enabled")
|
||||
host_port = host.get('port')
|
||||
host_version = host.get('version')
|
||||
host_username = host.get('user_name')
|
||||
host_auth_type = host.get('auth_type')
|
||||
host_auth_pass = host.get('auth_password')
|
||||
host_priv_type = host.get('privacy_type')
|
||||
host_priv_pass = host.get('privacy_password')
|
||||
present_state = host.get('state')
|
||||
current_hosts = self._current_config.get("current_hosts")
|
||||
current_entry = current_hosts.get(host_id)
|
||||
if present_state is not None:
|
||||
if present_state == 'absent':
|
||||
self._commands.append('no snmp-server host {0}' .format(host_id))
|
||||
return
|
||||
if host_enabled is not None:
|
||||
if current_entry.get('enabled') != host_enabled:
|
||||
if host_enabled is True:
|
||||
self._commands.append('no snmp-server host {0} disable' .format(host_id))
|
||||
else:
|
||||
self._commands.append('snmp-server host {0} disable' .format(host_id))
|
||||
if host_notification_type is not None:
|
||||
current_port = current_entry.get('port')
|
||||
current_version = current_entry.get('version')
|
||||
current_priv_type = current_entry.get('privacy_type')
|
||||
current_username = current_entry.get('user_name')
|
||||
current_auth_type = current_entry.get('auth_type')
|
||||
current_noti_type = current_entry.get('notification_type')
|
||||
if host_port is not None:
|
||||
if host_version is not None:
|
||||
if host_version == '3':
|
||||
if (host_priv_type is not None and host_priv_pass is not None):
|
||||
if((current_noti_type != host_notification_type) or
|
||||
((current_port != host_port)) or
|
||||
(current_version != host_version) or
|
||||
(current_priv_type != host_priv_type) or
|
||||
(current_username != host_username) or
|
||||
(current_auth_type != host_auth_type)):
|
||||
self._commands.append('snmp-server host {0} {1}s port {2} version {3} user {4} auth {5} {6} priv {7} {8}'
|
||||
.format(host_id, host_notification_type, host_port,
|
||||
host_version, host_username, host_auth_type, host_auth_pass,
|
||||
host_priv_type, host_priv_pass))
|
||||
else:
|
||||
if((current_noti_type != host_notification_type) or
|
||||
((current_port != host_port)) or
|
||||
(current_version != host_version) or
|
||||
(current_username != host_username) or
|
||||
(current_auth_type != host_auth_type)):
|
||||
self._commands.append('snmp-server host {0} {1}s port {2} version {3} user {4} auth {5} {6}'
|
||||
.format(host_id, host_notification_type,
|
||||
host_port, host_version, host_username,
|
||||
host_auth_type, host_auth_pass))
|
||||
else:
|
||||
if((current_noti_type != host_notification_type) or
|
||||
((current_port != host_port)) or
|
||||
(current_version != host_version)):
|
||||
self._commands.append('snmp-server host {0} {1}s port {2} version {3}'
|
||||
.format(host_id, host_notification_type,
|
||||
host_port, host_version))
|
||||
else:
|
||||
if ((current_noti_type != host_notification_type) or
|
||||
((current_port != host_port))):
|
||||
self._commands.append('snmp-server host {0} {1}s port {2}'
|
||||
.format(host_id, host_notification_type, host_port))
|
||||
else:
|
||||
if host_version is not None:
|
||||
if host_version == '3':
|
||||
if (host_priv_type is not None and host_priv_pass is not None):
|
||||
if ((current_noti_type != host_notification_type) or
|
||||
((current_version != host_version)) or
|
||||
(current_username != host_username) or
|
||||
((current_auth_type != host_auth_type)) or
|
||||
(current_priv_type != host_priv_type)):
|
||||
self._commands.append('snmp-server host {0} {1}s version {2} user {3} auth {4} {5} priv {6} {7}'
|
||||
.format(host_id, host_notification_type, host_version, host_username,
|
||||
host_auth_type, host_auth_pass, host_priv_type, host_priv_pass))
|
||||
|
||||
else:
|
||||
if ((current_noti_type != host_notification_type) or
|
||||
((current_version != host_version)) or
|
||||
(current_username != host_username) or
|
||||
((current_auth_type != host_auth_type))):
|
||||
self._commands.append('snmp-server host {0} {1}s version {2} user {3} auth {4} {5}'
|
||||
.format(host_id, host_notification_type,
|
||||
host_version, host_username, host_auth_type, host_auth_pass))
|
||||
|
||||
else:
|
||||
if ((current_noti_type != host_notification_type) or
|
||||
((current_version != host_version))):
|
||||
self._commands.append('snmp-server host {0} {1}s version {2}' .format(host_id,
|
||||
host_notification_type, host_version))
|
||||
|
||||
def generate_snmp_commands_without_current_config(self, host):
|
||||
host_id = host.get('name')
|
||||
host_notification_type = host.get('notification_type')
|
||||
host_enabled = host.get("enabled")
|
||||
host_port = host.get('port')
|
||||
host_version = host.get('version')
|
||||
host_username = host.get('user_name')
|
||||
host_auth_type = host.get('auth_type')
|
||||
host_auth_pass = host.get('auth_password')
|
||||
host_priv_type = host.get('privacy_type')
|
||||
host_priv_pass = host.get('privacy_password')
|
||||
present_state = host.get('state')
|
||||
present_state = host.get('state')
|
||||
if present_state is not None:
|
||||
if present_state == 'absent':
|
||||
return
|
||||
if host_enabled is not None:
|
||||
if host_enabled is True:
|
||||
self._commands.append('no snmp-server host {0} disable' .format(host_id))
|
||||
else:
|
||||
self._commands.append('snmp-server host {0} disable' .format(host_id))
|
||||
|
||||
if host_notification_type is not None:
|
||||
if host_port is not None:
|
||||
if host_version is not None:
|
||||
if host_version == '3':
|
||||
if (host_priv_type is not None and host_priv_pass is not None):
|
||||
self._commands.append('snmp-server host {0} {1}s port {2} version {3} user {4} auth {5} {6} priv {7} {8}'
|
||||
.format(host_id, host_notification_type, host_port, host_version, host_username,
|
||||
host_auth_type, host_auth_pass, host_priv_type, host_priv_pass))
|
||||
else:
|
||||
self._commands.append('snmp-server host {0} {1}s port {2} version {3} user {4} auth {5} {6}'
|
||||
.format(host_id, host_notification_type, host_port, host_version, host_username,
|
||||
host_auth_type, host_auth_pass))
|
||||
else:
|
||||
self._commands.append('snmp-server host {0} {1}s port {2} version {3}' .format(host_id,
|
||||
host_notification_type, host_port, host_version))
|
||||
else:
|
||||
self._commands.append('snmp-server host {0} {1}s port {2}' .format(host_id,
|
||||
host_notification_type, host_port))
|
||||
else:
|
||||
if host_version is not None:
|
||||
if host_version == '3':
|
||||
if (host_priv_type is not None and host_priv_pass is not None):
|
||||
self._commands.append('snmp-server host {0} {1}s version {2} user {3} auth {4} {5} priv {6} {7}'
|
||||
.format(host_id, host_notification_type, host_version, host_username,
|
||||
host_auth_type, host_auth_pass, host_priv_type, host_priv_pass))
|
||||
else:
|
||||
self._commands.append('snmp-server host {0} {1}s version {2} user {3} auth {4} {5}' .format(host_id,
|
||||
host_notification_type, host_version, host_username,
|
||||
host_auth_type, host_auth_pass))
|
||||
else:
|
||||
self._commands.append('snmp-server host {0} {1}s version {2}' .format(host_id,
|
||||
host_notification_type, host_version))
|
||||
|
||||
def generate_commands(self):
|
||||
req_hosts = self._required_config.get("hosts")
|
||||
host_names = self._current_config.get("host_names")
|
||||
|
||||
if req_hosts:
|
||||
for host in req_hosts:
|
||||
host_id = host.get('name')
|
||||
if host_id:
|
||||
if host_names and (host_id in host_names):
|
||||
self.generate_snmp_commands_with_current_config(host)
|
||||
else:
|
||||
self.generate_snmp_commands_without_current_config(host)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxSNMPHostsModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
277
plugins/modules/network/onyx/onyx_snmp_users.py
Normal file
277
plugins/modules/network/onyx/onyx_snmp_users.py
Normal file
@@ -0,0 +1,277 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_snmp_users
|
||||
author: "Sara Touqan (@sarato)"
|
||||
short_description: Configures SNMP User parameters
|
||||
description:
|
||||
- This module provides declarative management of SNMP Users protocol params
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
users:
|
||||
type: list
|
||||
description:
|
||||
- List of snmp users
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Specifies the name of the user.
|
||||
required: true
|
||||
type: str
|
||||
enabled:
|
||||
description:
|
||||
- Enables/Disables SNMP v3 access for the user.
|
||||
type: bool
|
||||
set_access_enabled:
|
||||
description:
|
||||
- Enables/Disables SNMP SET requests for the user.
|
||||
type: bool
|
||||
require_privacy:
|
||||
description:
|
||||
- Enables/Disables the Require privacy (encryption) for requests from this user
|
||||
type: bool
|
||||
auth_type:
|
||||
description:
|
||||
- Configures the hash type used to configure SNMP v3 security parameters.
|
||||
choices: ['md5', 'sha', 'sha224', 'sha256', 'sha384', 'sha512']
|
||||
type: str
|
||||
auth_password:
|
||||
description:
|
||||
- The password needed to configure the hash type.
|
||||
type: str
|
||||
capability_level:
|
||||
description:
|
||||
- Sets capability level for SET requests.
|
||||
choices: ['admin','monitor','unpriv','v_admin']
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: enables snmp user
|
||||
onyx_snmp_users:
|
||||
users:
|
||||
- name: sara
|
||||
enabled: true
|
||||
|
||||
- name: enables snmp set requests
|
||||
onyx_snmp_users:
|
||||
users:
|
||||
- name: sara
|
||||
set_access_enabled: yes
|
||||
|
||||
- name: enables user require privacy
|
||||
onyx_snmp_users:
|
||||
users:
|
||||
- name: sara
|
||||
require_privacy: true
|
||||
|
||||
- name: configures user hash type
|
||||
onyx_snmp_users:
|
||||
users:
|
||||
- auth_type: md5
|
||||
auth_password: 1297sara1234sara
|
||||
|
||||
- name: configures user capability_level
|
||||
onyx_snmp_users:
|
||||
users:
|
||||
- name: sara
|
||||
capability_level: admin
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- snmp-server user <user_name> v3 enable
|
||||
- no snmp-server user <user_name> v3 enable
|
||||
- snmp-server user <user_name> v3 enable sets
|
||||
- no snmp-server user <user_name> v3 enable sets
|
||||
- snmp-server user <user_name> v3 require-privacy
|
||||
- no snmp-server user <user_name> v3 require-privacy
|
||||
- snmp-server user <user_name> v3 capability <capability_level>
|
||||
- snmp-server user <user_name> v3 auth <hash_type> <password>
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxSNMPUsersModule(BaseOnyxModule):
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
user_spec = dict(name=dict(required=True),
|
||||
enabled=dict(type='bool'),
|
||||
set_access_enabled=dict(type='bool'),
|
||||
require_privacy=dict(type='bool'),
|
||||
auth_type=dict(type='str', choices=['md5', 'sha', 'sha224', 'sha256', 'sha384', 'sha512']),
|
||||
auth_password=dict(type='str'),
|
||||
capability_level=dict(type='str', choices=['admin', 'monitor', 'unpriv', 'v_admin']),
|
||||
)
|
||||
element_spec = dict(
|
||||
users=dict(type='list', elements='dict', options=user_spec),
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _set_snmp_config(self, users_config):
|
||||
if users_config[0]:
|
||||
if users_config[0].get('Lines'):
|
||||
return
|
||||
current_users = []
|
||||
count = 0
|
||||
enabled = True
|
||||
set_access_enabled = True
|
||||
require_privacy = True
|
||||
auth_type = ''
|
||||
capability_level = ''
|
||||
name = ''
|
||||
all_users_names = []
|
||||
for user in users_config:
|
||||
user_dict = {}
|
||||
entry_dict = {}
|
||||
for entry in user:
|
||||
name = entry.split()[2]
|
||||
if user.get(entry):
|
||||
if user.get(entry)[0]:
|
||||
enabled = user.get(entry)[0].get('Enabled overall')
|
||||
if enabled == 'no':
|
||||
enabled = False
|
||||
else:
|
||||
enabled = True
|
||||
set_access_enabled = user.get(entry)[1].get('SET access')[0].get('Enabled')
|
||||
if set_access_enabled == 'no':
|
||||
set_access_enabled = False
|
||||
else:
|
||||
set_access_enabled = True
|
||||
require_privacy = user.get(entry)[0].get('Require privacy')
|
||||
if require_privacy == 'yes':
|
||||
require_privacy = True
|
||||
else:
|
||||
require_privacy = False
|
||||
capability_level = user.get(entry)[1].get('SET access')[0].get('Capability level')
|
||||
auth_type = user.get(entry)[0].get('Authentication type')
|
||||
user_dict['enabled'] = enabled
|
||||
user_dict['set_access_enabled'] = set_access_enabled
|
||||
user_dict['auth_type'] = auth_type
|
||||
user_dict['capability_level'] = capability_level
|
||||
user_dict['require_privacy'] = require_privacy
|
||||
entry_dict[name] = user_dict
|
||||
all_users_names.append(name)
|
||||
current_users.append(entry_dict)
|
||||
self._current_config['users'] = current_users
|
||||
self._current_config['current_names'] = all_users_names
|
||||
|
||||
def _show_users(self):
|
||||
cmd = "show snmp user"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
users_config = self._show_users()
|
||||
if users_config:
|
||||
self._set_snmp_config(users_config)
|
||||
|
||||
def generate_commands(self):
|
||||
req_uers = self._required_config.get("users")
|
||||
current_users = self._current_config.get("users")
|
||||
current_names = self._current_config.get("current_names")
|
||||
if req_uers:
|
||||
for user in req_uers:
|
||||
user_id = user.get('name')
|
||||
if user_id:
|
||||
if current_names and (user_id in current_names):
|
||||
for user_entry in current_users:
|
||||
for user_name in user_entry:
|
||||
if user_name == user_id:
|
||||
user_state = user.get("enabled")
|
||||
user_entry_name = user_entry.get(user_name)
|
||||
if user_state is not None:
|
||||
if user_state != user_entry_name.get("enabled"):
|
||||
if user_state is True:
|
||||
self._commands.append('snmp-server user {0} v3 enable' .format(user_id))
|
||||
else:
|
||||
self._commands.append('no snmp-server user {0} v3 enable' .format(user_id))
|
||||
set_state = user.get("set_access_enabled")
|
||||
if set_state is not None:
|
||||
if set_state != user_entry_name.get("set_access_enabled"):
|
||||
if set_state is True:
|
||||
self._commands.append('snmp-server user {0} v3 enable sets' .format(user_id))
|
||||
else:
|
||||
self._commands.append('no snmp-server user {0} v3 enable sets' .format(user_id))
|
||||
auth_type = user.get("auth_type")
|
||||
if auth_type is not None:
|
||||
if user.get("auth_password") is not None:
|
||||
if auth_type != user_entry_name.get("auth_type"):
|
||||
self._commands.append('snmp-server user {0} v3 auth {1} {2}'
|
||||
.format(user_id, user.get('auth_type'), user.get('auth_password')))
|
||||
cap_level = user.get("capability_level")
|
||||
if cap_level is not None:
|
||||
if cap_level != user_entry_name.get("capability_level"):
|
||||
self._commands.append('snmp-server user {0} v3 capability {1}'
|
||||
.format(user_id, user.get('capability_level')))
|
||||
req_priv = user.get("require_privacy")
|
||||
if req_priv is not None:
|
||||
if req_priv != user_entry_name.get("require_privacy"):
|
||||
if req_priv is True:
|
||||
self._commands.append('snmp-server user {0} v3 require-privacy' .format(user_id))
|
||||
else:
|
||||
self._commands.append('no snmp-server user {0} v3 require-privacy' .format(user_id))
|
||||
|
||||
else:
|
||||
user_state = user.get("enabled")
|
||||
if user_state is not None:
|
||||
if user_state is True:
|
||||
self._commands.append('snmp-server user {0} v3 enable' .format(user_id))
|
||||
else:
|
||||
self._commands.append('no snmp-server user {0} v3 enable' .format(user_id))
|
||||
set_state = user.get("set_access_enabled")
|
||||
if set_state is not None:
|
||||
if set_state is True:
|
||||
self._commands.append('snmp-server user {0} v3 enable sets' .format(user_id))
|
||||
else:
|
||||
self._commands.append('no snmp-server user {0} v3 enable sets' .format(user_id))
|
||||
if user.get("capability_level") is not None:
|
||||
self._commands.append('snmp-server user {0} v3 capability {1}' .format(user_id, user.get('capability_level')))
|
||||
req_priv = user.get("require_privacy")
|
||||
if req_priv is not None:
|
||||
if req_priv is True:
|
||||
self._commands.append('snmp-server user {0} v3 require-privacy' .format(user_id))
|
||||
else:
|
||||
self._commands.append('no snmp-server user {0} v3 require-privacy' .format(user_id))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxSNMPUsersModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
251
plugins/modules/network/onyx/onyx_syslog_files.py
Normal file
251
plugins/modules/network/onyx/onyx_syslog_files.py
Normal file
@@ -0,0 +1,251 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: onyx_syslog_files
|
||||
author: "Anas Shami (@anass)"
|
||||
short_description: Configure file management syslog module
|
||||
description:
|
||||
- This module provides declarative management of syslog
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
options:
|
||||
debug:
|
||||
description:
|
||||
- Configure settings for debug log files
|
||||
type: bool
|
||||
default: False
|
||||
delete_group:
|
||||
description:
|
||||
- Delete certain log files
|
||||
choices: ['current', 'oldest']
|
||||
type: str
|
||||
rotation:
|
||||
description:
|
||||
- rotation related attributes
|
||||
type: dict
|
||||
suboptions:
|
||||
frequency:
|
||||
description:
|
||||
- Rotate log files on a fixed time-based schedule
|
||||
choices: ['daily', 'weekly', 'monthly']
|
||||
type: str
|
||||
force:
|
||||
description:
|
||||
- force an immediate rotation of log files
|
||||
type: bool
|
||||
max_num:
|
||||
description:
|
||||
- Sepcify max_num of old log files to keep
|
||||
type: int
|
||||
size:
|
||||
description:
|
||||
- Rotate files when they pass max size
|
||||
type: float
|
||||
size_pct:
|
||||
description:
|
||||
- Rotatoe files when they pass percent of HD
|
||||
type: float
|
||||
upload_url:
|
||||
description:
|
||||
- upload local log files to remote host (ftp, scp, sftp, tftp) with format protocol://username[:password]@server/path
|
||||
type: str
|
||||
upload_file:
|
||||
description:
|
||||
- Upload compressed log file (current or filename)
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: syslog delete old files
|
||||
- onyx_syslog_files:
|
||||
delete_group: oldest
|
||||
- name: syslog upload file
|
||||
- onyx_syslog_files:
|
||||
upload_url: scp://username:password@hostnamepath/filename
|
||||
upload_file: current
|
||||
- name: syslog rotation force, frequency and max number
|
||||
- onyx_syslog_files:
|
||||
rotation:
|
||||
force: true
|
||||
max_num: 30
|
||||
frequency: daily
|
||||
size: 128
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- logging files delete current
|
||||
- logging files rotate criteria
|
||||
- logging files upload current url
|
||||
"""
|
||||
import re
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxSyslogFilesModule(BaseOnyxModule):
|
||||
MAX_FILES = 999999
|
||||
URL_REGEX = re.compile(
|
||||
r'^(ftp|scp|ftps):\/\/[a-z0-9\.]*:(.*)@(.*):([a-zA-Z\/\/])*$')
|
||||
FREQUANCIES = ['daily', 'weekly', 'monthly']
|
||||
ROTATION_KEYS = ['frequency', 'max_num', 'size', 'size_pct', 'force']
|
||||
ROTATION_CMDS = {'size': 'logging {0} rotation criteria size {1}',
|
||||
'frequency': 'logging {0} rotation criteria frequency {1}',
|
||||
'max_num': 'logging {0} rotation max-num {1}',
|
||||
'size_pct': 'logging {0} rotation criteria size-pct {1}',
|
||||
'force': 'logging {0} rotation force'}
|
||||
|
||||
def init_module(self):
|
||||
"""" Ansible module initialization
|
||||
"""
|
||||
rotation_spec = dict(frequency=dict(choices=self.FREQUANCIES),
|
||||
max_num=dict(type="int"),
|
||||
force=dict(type="bool"),
|
||||
size=dict(type="float"),
|
||||
size_pct=dict(type="float"))
|
||||
|
||||
element_spec = dict(delete_group=dict(choices=['oldest', 'current']),
|
||||
rotation=dict(type="dict", options=rotation_spec),
|
||||
upload_file=dict(type="str"),
|
||||
upload_url=dict(type="str"),
|
||||
debug=dict(type="bool", default=False))
|
||||
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_together=[['upload_file', 'upload_url']])
|
||||
|
||||
def validate_rotation(self, rotation):
|
||||
size_pct = rotation.get('size_pct', None)
|
||||
max_num = rotation.get('max_num', None)
|
||||
if size_pct is not None and (float(size_pct) < 0 or float(size_pct) > 100):
|
||||
self._module.fail_json(
|
||||
msg='logging size_pct must be in range 0-100')
|
||||
elif max_num is not None and (int(max_num) < 0 or int(max_num) > self.MAX_FILES):
|
||||
self._module.fail_json(
|
||||
msg='logging max_num must be positive number less than {0}'.format(self.MAX_FILES))
|
||||
|
||||
def validate_upload_url(self, upload_url):
|
||||
check = self.URL_REGEX.match(upload_url)
|
||||
if upload_url and not check:
|
||||
self._module.fail_json(
|
||||
msg='Invalid url, make sure that you use "[ftp, scp, tftp, sftp]://username:password@hostname:/location" format')
|
||||
|
||||
def show_logging(self):
|
||||
show_logging = show_cmd(self._module, "show logging", json_fmt=True, fail_on_error=False)
|
||||
running_config = show_cmd(self._module, "show running-config | include .*logging.*debug-files.*", json_fmt=True, fail_on_error=False)
|
||||
|
||||
if len(show_logging) > 0:
|
||||
show_logging[0]['debug'] = running_config['Lines'] if 'Lines' in running_config else []
|
||||
else:
|
||||
show_logging = [{
|
||||
'debug': running_config['Lines'] if 'Lines' in running_config else []
|
||||
}]
|
||||
return show_logging
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
current_config = self.show_logging()[0]
|
||||
freq = current_config.get('Log rotation frequency') # daily (Once per day at midnight)
|
||||
size = current_config.get('Log rotation size threshold') # 19.07 megabytes or 10.000% of partition (987.84 megabytes)
|
||||
max_num = current_config.get('Number of archived log files to keep')
|
||||
if freq is not None:
|
||||
freq_str = freq.split()[0]
|
||||
self._current_config['frequency'] = freq_str
|
||||
|
||||
if size is not None:
|
||||
size_arr = size.split(' ')
|
||||
if '%' in size:
|
||||
size_pct_value = size_arr[0].replace('%', '')
|
||||
self._current_config['size_pct'] = float(size_pct_value)
|
||||
size_value = re.sub(r'(\(|\)|megabytes)', '', size_arr[-2]).strip()
|
||||
self._current_config['size'] = float(size_value)
|
||||
else:
|
||||
size_value = size_arr[0]
|
||||
self._current_config['size'] = float(size_value)
|
||||
|
||||
if max_num is not None:
|
||||
self._current_config['max_num'] = int(max_num)
|
||||
|
||||
'''debug params'''
|
||||
for line in current_config['debug']:
|
||||
if 'size' in line:
|
||||
self._current_config['debug_size'] = float(line.split(' ')[-1])
|
||||
elif 'frequency' in line:
|
||||
self._current_config['debug_frequency'] = line.split(' ')[-1]
|
||||
elif 'size-pct' in line:
|
||||
self._current_config['debug_size_pct'] = float(line.split(' ')[-1])
|
||||
elif 'max-num' in line:
|
||||
self._current_config['debug_max_num'] = int(line.split(' ')[-1])
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = dict()
|
||||
required_config = dict()
|
||||
module_params = self._module.params
|
||||
|
||||
delete_group = module_params.get('delete_group')
|
||||
upload_file = module_params.get('upload_file')
|
||||
rotation = module_params.get('rotation')
|
||||
if delete_group:
|
||||
required_config['delete_group'] = delete_group
|
||||
if upload_file:
|
||||
required_config.update({'upload_file': upload_file,
|
||||
'upload_url': module_params.get('upload_url')})
|
||||
if rotation:
|
||||
required_config['rotation'] = rotation
|
||||
required_config['debug'] = module_params['debug']
|
||||
|
||||
self.validate_param_values(required_config)
|
||||
self._required_config = required_config
|
||||
|
||||
def generate_commands(self):
|
||||
required_config = self._required_config
|
||||
current_config = self._current_config
|
||||
|
||||
logging_files_type = 'debug-files' if required_config['debug'] else 'files'
|
||||
debug_prefix = 'debug_' if required_config['debug'] else ''
|
||||
|
||||
rotation = required_config.get('rotation')
|
||||
if rotation:
|
||||
for key in rotation:
|
||||
if rotation.get(key) and current_config.get(debug_prefix + key) != rotation.get(key):
|
||||
cmd = self.ROTATION_CMDS[key].format(logging_files_type, rotation[key]) if key != 'force' else\
|
||||
self.ROTATION_CMDS[key].format(logging_files_type)
|
||||
self._commands.append(cmd)
|
||||
|
||||
delete_group = required_config.get('delete_group')
|
||||
if delete_group:
|
||||
self._commands.append('logging {0} delete {1}'.format(logging_files_type,
|
||||
delete_group))
|
||||
|
||||
upload_file = required_config.get('upload_file')
|
||||
if upload_file:
|
||||
self._commands.append('logging {0} upload {1} {2}'.format(logging_files_type,
|
||||
upload_file, required_config.get('upload_url')))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxSyslogFilesModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
349
plugins/modules/network/onyx/onyx_syslog_remote.py
Normal file
349
plugins/modules/network/onyx/onyx_syslog_remote.py
Normal file
@@ -0,0 +1,349 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: onyx_syslog_remote
|
||||
author: "Anas Shami (@anass)"
|
||||
short_description: Configure remote syslog module
|
||||
description:
|
||||
- This module provides declarative management of syslog
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
options:
|
||||
enabled:
|
||||
description:
|
||||
- Disable/Enable logging to given remote host
|
||||
default: true
|
||||
type: bool
|
||||
host:
|
||||
description:
|
||||
- <IP4/IP6 Hostname> Send event logs to this server using the syslog protocol
|
||||
required: true
|
||||
type: str
|
||||
port:
|
||||
description:
|
||||
- Set remote server destination port for log messages
|
||||
type: int
|
||||
trap:
|
||||
description:
|
||||
- Minimum severity level for messages to this syslog server
|
||||
choices: ['none', 'debug', 'info', 'notice', 'alert', 'warning', 'err', 'emerg', 'crit']
|
||||
type: str
|
||||
trap_override:
|
||||
description:
|
||||
- Override log levels for this sink on a per-class basis
|
||||
type: list
|
||||
suboptions:
|
||||
override_class:
|
||||
description:
|
||||
- Specify a class whose log level to override
|
||||
choices: ['mgmt-front', 'mgmt-back', 'mgmt-core', 'events', 'debug-module', 'sx-sdk', 'mlx-daemons', 'protocol-stack']
|
||||
required: True
|
||||
type: str
|
||||
override_priority:
|
||||
description:
|
||||
-Specify a priority whose log level to override
|
||||
choices: ['none', 'debug', 'info', 'notice', 'alert', 'warning', 'err', 'emerg', 'crit']
|
||||
type: str
|
||||
override_enabled:
|
||||
description:
|
||||
- disable override priorities for specific class.
|
||||
default: True
|
||||
type: bool
|
||||
|
||||
filter:
|
||||
description:
|
||||
- Specify a filter type
|
||||
choices: ['include', 'exclude']
|
||||
type: str
|
||||
filter_str:
|
||||
description:
|
||||
- Specify a regex filter string
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: remote logging port 8080
|
||||
- onyx_syslog_remote:
|
||||
host: 10.10.10.10
|
||||
port: 8080
|
||||
|
||||
- name: remote logging trap override
|
||||
- onyx_syslog_remote:
|
||||
host: 10.10.10.10
|
||||
trap_override:
|
||||
- override_class: events
|
||||
override_priority: emerg
|
||||
|
||||
- name: remote logging trap emerg
|
||||
- onyx_syslog_remote:
|
||||
host: 10.10.10.10
|
||||
trap: emerg
|
||||
|
||||
- name: remote logging filter include 'ERR'
|
||||
- onyx_syslog_remote:
|
||||
host: 10.10.10.10
|
||||
filter: include
|
||||
filter_str: /ERR/
|
||||
|
||||
- name: disable remote logging with class events
|
||||
- onyx_syslog_remote:
|
||||
enabled: False
|
||||
host: 10.10.10.10
|
||||
class: events
|
||||
- name : disable remote logging
|
||||
- onyx_syslog_remote:
|
||||
enabled: False
|
||||
host: 10.10.10.10
|
||||
|
||||
- name : enable/disable override class
|
||||
- onyx_syslog_remote:
|
||||
host: 10.7.144.71
|
||||
trap_override:
|
||||
- override_class: events
|
||||
override_priority: emerg
|
||||
override_enabled: False
|
||||
- override_class: mgmt-front
|
||||
override_priority: alert
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- logging x port 8080
|
||||
- logging 10.10.10.10 trap override class events priority emerg
|
||||
- no logging 10.10.10.10 trap override class events
|
||||
- logging 10.10.10.10 trap emerg
|
||||
- logging 10.10.10.10 filter [include | exclude] ERR
|
||||
"""
|
||||
|
||||
import re
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxSyslogRemoteModule(BaseOnyxModule):
|
||||
MAX_PORT = 65535
|
||||
LEVELS = ['none', 'debug', 'info', 'notice', 'alert', 'warning', 'err', 'emerg', 'crit']
|
||||
CLASSES = ['mgmt-front', 'mgmt-back', 'mgmt-core', 'events', 'debug-module', 'sx-sdk', 'mlx-daemons', 'protocol-stack']
|
||||
FILTER = ['include', 'exclude']
|
||||
|
||||
LOGGING_HOST = re.compile(r'^logging ([a-z0-9\.]+)$')
|
||||
LOGGING_PORT = re.compile(r'^logging ([a-z0-9\.]+) port ([0-9]+)$')
|
||||
LOGGING_TRAP = re.compile(r'^logging ([a-z0-9\.]+) trap ([a-z]+)$')
|
||||
LOGGING_TRAP_OVERRIDE = re.compile(r'^logging ([a-z0-9\.]+) trap override class ([a-z\-]+) priority ([a-z]+)$')
|
||||
LOGGING_FILTER = re.compile(r'^logging ([a-z0-9\.]+) filter (include|exclude) "([\D\d]+)"$')
|
||||
|
||||
def init_module(self):
|
||||
"""" Ansible module initialization
|
||||
"""
|
||||
override_spec = dict(override_priority=dict(choices=self.LEVELS),
|
||||
override_class=dict(choices=self.CLASSES, required=True),
|
||||
override_enabled=dict(default=True, type="bool"))
|
||||
|
||||
element_spec = dict(enabled=dict(type="bool", default=True),
|
||||
host=dict(type="str", required=True),
|
||||
port=dict(type="int"),
|
||||
trap=dict(choices=self.LEVELS),
|
||||
trap_override=dict(type="list", elements='dict', options=override_spec),
|
||||
filter=dict(choices=self.FILTER),
|
||||
filter_str=dict(type="str"))
|
||||
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_together=[
|
||||
['filter', 'filter_str']
|
||||
])
|
||||
|
||||
def validate_port(self, port):
|
||||
if port and (port < 1 or port > self.MAX_PORT):
|
||||
self._module.fail_json(msg='logging port must be between 1 and {0}'.format(self.MAX_PORT))
|
||||
|
||||
def show_logging(self):
|
||||
# we can't use show logging it has lack of information
|
||||
return show_cmd(self._module, "show running-config | include .*logging.*", json_fmt=False, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
current_config = self.show_logging().split('\n')
|
||||
for line in current_config:
|
||||
line = line.strip()
|
||||
match = self.LOGGING_HOST.match(line)
|
||||
if match:
|
||||
host = match.group(1)
|
||||
self._current_config[host] = dict()
|
||||
continue
|
||||
|
||||
match = self.LOGGING_PORT.match(line)
|
||||
if match:
|
||||
host = match.group(1)
|
||||
port = int(match.group(2))
|
||||
if host in self._current_config:
|
||||
self._current_config[host]['port'] = port
|
||||
else:
|
||||
self._current_config[host] = dict(port=port)
|
||||
continue
|
||||
|
||||
match = self.LOGGING_TRAP.match(line)
|
||||
if match:
|
||||
host = match.group(1)
|
||||
trap = match.group(2)
|
||||
host_config = self._current_config.get(host)
|
||||
if host_config:
|
||||
self._current_config[host]['trap'] = trap
|
||||
else:
|
||||
self._current_config[host] = dict(trap=trap)
|
||||
continue
|
||||
|
||||
match = self.LOGGING_TRAP_OVERRIDE.match(line)
|
||||
if match:
|
||||
host = match.group(1)
|
||||
override_class = match.group(2)
|
||||
override_priority = match.group(3)
|
||||
host_config = self._current_config.get(host)
|
||||
|
||||
if host_config:
|
||||
if 'trap_override' in host_config:
|
||||
self._current_config[host]['trap_override'].append(dict(override_class=override_class, override_priority=override_priority))
|
||||
else:
|
||||
self._current_config[host]['trap_override'] = [dict(override_class=override_class, override_priority=override_priority)]
|
||||
else:
|
||||
self._current_config[host] = {'trap_override': [dict(override_class=override_class, override_priority=override_priority)]}
|
||||
continue
|
||||
|
||||
match = self.LOGGING_FILTER.match(line)
|
||||
if match:
|
||||
host = match.group(1)
|
||||
filter_type = match.group(2)
|
||||
filter_str = match.group(3)
|
||||
if host in self._current_config:
|
||||
self._current_config[host].update({'filter': filter_type, 'filter_str': filter_str})
|
||||
else:
|
||||
self._current_config[host] = dict(filter=filter_type, filter_str=filter_str)
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = dict()
|
||||
required_config = dict()
|
||||
module_params = self._module.params
|
||||
port = module_params.get('port')
|
||||
trap = module_params.get('trap')
|
||||
trap_override = module_params.get('trap_override')
|
||||
filtered = module_params.get('filter')
|
||||
|
||||
required_config['host'] = module_params.get('host')
|
||||
required_config['enabled'] = module_params.get('enabled')
|
||||
|
||||
if port:
|
||||
required_config['port'] = port
|
||||
if trap:
|
||||
required_config['trap'] = trap
|
||||
if trap_override:
|
||||
required_config['trap_override'] = trap_override
|
||||
if filtered:
|
||||
required_config['filter'] = filtered
|
||||
required_config['filter_str'] = module_params.get('filter_str', '')
|
||||
|
||||
self.validate_param_values(required_config)
|
||||
self._required_config = required_config
|
||||
|
||||
def generate_commands(self):
|
||||
required_config = self._required_config
|
||||
current_config = self._current_config
|
||||
host = required_config.get('host')
|
||||
enabled = required_config['enabled']
|
||||
'''
|
||||
cases:
|
||||
if host in current config and current config != required config and its enable
|
||||
if host in current config and its disable
|
||||
if host in current and it has override_class with disable flag
|
||||
'''
|
||||
host_config = current_config.get(host, dict())
|
||||
|
||||
if host in current_config and not enabled:
|
||||
self._commands.append('no logging {0}'.format(host))
|
||||
else:
|
||||
if host not in current_config:
|
||||
self._commands.append('logging {0}'.format(host))
|
||||
if 'port' in required_config:
|
||||
if required_config['port'] != host_config.get('port', None) or not host_config:
|
||||
'''Edit/Create new one'''
|
||||
self._commands.append('logging {0} port {1}'.format(host, required_config['port']))
|
||||
|
||||
if 'trap' in required_config or 'trap_override' in required_config:
|
||||
trap_commands = self._get_trap_commands(host)
|
||||
self._commands += trap_commands
|
||||
|
||||
if 'filter' in required_config:
|
||||
is_change = host_config.get('filter', None) != required_config['filter'] or \
|
||||
host_config.get('filter_str', None) != required_config['filter_str']
|
||||
if is_change or not host_config:
|
||||
self._commands.append('logging {0} filter {1} {2}'.format(host, required_config['filter'], required_config['filter_str']))
|
||||
|
||||
''' ********** private methods ********** '''
|
||||
def _get_trap_commands(self, host):
|
||||
current_config = self._current_config
|
||||
required_config = self._required_config
|
||||
trap_commands = []
|
||||
host_config = current_config.get(host, dict())
|
||||
|
||||
override_list = required_config.get('trap_override')
|
||||
if override_list:
|
||||
current_override_list = host_config.get('trap_override', [])
|
||||
|
||||
for override_trap in override_list:
|
||||
override_class = override_trap.get('override_class')
|
||||
override_priority = override_trap.get('override_priority')
|
||||
override_enabled = override_trap.get('override_enabled')
|
||||
found, found_class = False, False
|
||||
for current_override in current_override_list:
|
||||
if current_override.get('override_class') == override_class:
|
||||
found_class = True
|
||||
if not override_enabled:
|
||||
break
|
||||
if override_priority and current_override.get('override_priority') == override_priority:
|
||||
found = True
|
||||
break
|
||||
|
||||
if override_enabled:
|
||||
if not found and override_priority:
|
||||
trap_commands.append('logging {0} trap override class {1} priority {2}'.format(
|
||||
host, override_class, override_priority))
|
||||
elif found_class: # disabled option will use only class
|
||||
trap_commands.append('no logging {0} trap override class {1}'.format(
|
||||
host, override_class))
|
||||
|
||||
else:
|
||||
if required_config['enabled']: # no disabled option for this, just override trap level can be disabled
|
||||
trap = required_config.get('trap')
|
||||
if trap and (trap != host_config.get('trap', None) or not host_config):
|
||||
trap_commands.append('logging {0} trap {1}'.format(
|
||||
host, trap))
|
||||
'''no disable for trap'''
|
||||
|
||||
return trap_commands
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxSyslogRemoteModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
325
plugins/modules/network/onyx/onyx_traffic_class.py
Normal file
325
plugins/modules/network/onyx/onyx_traffic_class.py
Normal file
@@ -0,0 +1,325 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_traffic_class
|
||||
author: "Anas Badaha (@anasb)"
|
||||
short_description: Configures Traffic Class
|
||||
description:
|
||||
- This module provides declarative management of Traffic Class configuration
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- enable congestion control on interface.
|
||||
choices: ['enabled', 'disabled']
|
||||
default: enabled
|
||||
interfaces:
|
||||
description:
|
||||
- list of interfaces name.
|
||||
required: true
|
||||
tc:
|
||||
description:
|
||||
- traffic class, range 0-7.
|
||||
required: true
|
||||
congestion_control:
|
||||
description:
|
||||
- configure congestion control on interface.
|
||||
suboptions:
|
||||
control:
|
||||
description:
|
||||
- congestion control type.
|
||||
choices: ['red', 'ecn', 'both']
|
||||
required: true
|
||||
threshold_mode:
|
||||
description:
|
||||
- congestion control threshold mode.
|
||||
choices: ['absolute', 'relative']
|
||||
required: true
|
||||
min_threshold:
|
||||
description:
|
||||
- Set minimum-threshold value (in KBs) for marking traffic-class queue.
|
||||
required: true
|
||||
max_threshold:
|
||||
description:
|
||||
- Set maximum-threshold value (in KBs) for marking traffic-class queue.
|
||||
required: true
|
||||
dcb:
|
||||
description:
|
||||
- configure dcb control on interface.
|
||||
suboptions:
|
||||
mode:
|
||||
description:
|
||||
- dcb control mode.
|
||||
choices: ['strict', 'wrr']
|
||||
required: true
|
||||
weight:
|
||||
description:
|
||||
- Relevant only for wrr mode.
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure traffic class
|
||||
onyx_traffic_class:
|
||||
interfaces:
|
||||
- Eth1/1
|
||||
- Eth1/2
|
||||
tc: 3
|
||||
congestion_control:
|
||||
control: ecn
|
||||
threshold_mode: absolute
|
||||
min_threshold: 500
|
||||
max_threshold: 1500
|
||||
dcb:
|
||||
mode: strict
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- interface ethernet 1/15 traffic-class 3 congestion-control ecn minimum-absolute 150 maximum-absolute 1500
|
||||
- interface ethernet 1/16 traffic-class 3 congestion-control ecn minimum-absolute 150 maximum-absolute 1500
|
||||
- interface mlag-port-channel 7 traffic-class 3 congestion-control ecn minimum-absolute 150 maximum-absolute 1500
|
||||
- interface port-channel 1 traffic-class 3 congestion-control ecn minimum-absolute 150 maximum-absolute 1500
|
||||
- interface ethernet 1/15 traffic-class 3 dcb ets strict
|
||||
- interface ethernet 1/16 traffic-class 3 dcb ets strict
|
||||
- interface mlag-port-channel 7 traffic-class 3 dcb ets strict
|
||||
- interface port-channel 1 traffic-class 3 dcb ets strict
|
||||
"""
|
||||
|
||||
import re
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxTrafficClassModule(BaseOnyxModule):
|
||||
|
||||
IF_ETH_REGEX = re.compile(r"^Eth(\d+\/\d+|Eth\d+\/\d+\d+)$")
|
||||
IF_PO_REGEX = re.compile(r"^Po(\d+)$")
|
||||
MLAG_NAME_REGEX = re.compile(r"^Mpo(\d+)$")
|
||||
|
||||
IF_TYPE_ETH = "ethernet"
|
||||
PORT_CHANNEL = "port-channel"
|
||||
MLAG_PORT_CHANNEL = "mlag-port-channel"
|
||||
|
||||
IF_TYPE_MAP = {
|
||||
IF_TYPE_ETH: IF_ETH_REGEX,
|
||||
PORT_CHANNEL: IF_PO_REGEX,
|
||||
MLAG_PORT_CHANNEL: MLAG_NAME_REGEX
|
||||
}
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
congestion_control_spec = dict(control=dict(choices=['red', 'ecn', 'both'], required=True),
|
||||
threshold_mode=dict(choices=['absolute', 'relative'], required=True),
|
||||
min_threshold=dict(type=int, required=True),
|
||||
max_threshold=dict(type=int, required=True))
|
||||
|
||||
dcb_spec = dict(mode=dict(choices=['strict', 'wrr'], required=True),
|
||||
weight=dict(type=int))
|
||||
|
||||
element_spec = dict(
|
||||
interfaces=dict(type='list', required=True),
|
||||
tc=dict(type=int, required=True),
|
||||
congestion_control=dict(type='dict', options=congestion_control_spec),
|
||||
dcb=dict(type='dict', options=dcb_spec),
|
||||
state=dict(choices=['enabled', 'disabled'], default='enabled'))
|
||||
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def validate_tc(self, value):
|
||||
if value and not 0 <= int(value) <= 7:
|
||||
self._module.fail_json(msg='tc value must be between 0 and 7')
|
||||
|
||||
def validate_param_values(self, obj, param=None):
|
||||
dcb = obj.get("dcb")
|
||||
if dcb is not None:
|
||||
dcb_mode = dcb.get("mode")
|
||||
weight = dcb.get("weight")
|
||||
if dcb_mode == "wrr" and weight is None:
|
||||
self._module.fail_json(msg='User should send weight attribute when dcb mode is wrr')
|
||||
super(OnyxTrafficClassModule, self).validate_param_values(obj, param)
|
||||
|
||||
def _get_interface_type(self, if_name):
|
||||
if_type = None
|
||||
if_id = None
|
||||
for interface_type, interface_regex in iteritems(self.IF_TYPE_MAP):
|
||||
match = interface_regex.match(if_name)
|
||||
if match:
|
||||
if_type = interface_type
|
||||
if_id = match.group(1)
|
||||
break
|
||||
return if_type, if_id
|
||||
|
||||
def _set_interface_congestion_control_config(self, interface_congestion_control_config,
|
||||
interface, if_type, if_id):
|
||||
tc = self._required_config.get("tc")
|
||||
interface_dcb_ets = self._show_interface_dcb_ets(if_type, if_id)[0].get(interface)
|
||||
if interface_dcb_ets is None:
|
||||
dcb = dict()
|
||||
else:
|
||||
ets_per_tc = interface_dcb_ets[2].get("ETS per TC")
|
||||
tc_config = ets_per_tc[0].get(str(tc))
|
||||
dcb_mode = tc_config[0].get("S.Mode")
|
||||
dcb_weight = int(tc_config[0].get("W"))
|
||||
dcb = dict(mode=dcb_mode.lower(), weight=dcb_weight)
|
||||
|
||||
interface_congestion_control_config = interface_congestion_control_config[tc + 1]
|
||||
mode = interface_congestion_control_config.get("Mode")
|
||||
if mode == "none":
|
||||
self._current_config[interface] = dict(state="disabled", dcb=dcb, if_type=if_type, if_id=if_id)
|
||||
return
|
||||
|
||||
threshold_mode = interface_congestion_control_config.get("Threshold mode")
|
||||
max_threshold = interface_congestion_control_config.get("Maximum threshold")
|
||||
min_threshold = interface_congestion_control_config.get("Minimum threshold")
|
||||
|
||||
if threshold_mode == "absolute":
|
||||
delimiter = ' '
|
||||
else:
|
||||
delimiter = '%'
|
||||
min_value = int(min_threshold.split(delimiter)[0])
|
||||
max_malue = int(max_threshold.split(delimiter)[0])
|
||||
congestion_control = dict(control=mode.lower(), threshold_mode=threshold_mode,
|
||||
min_threshold=min_value, max_threshold=max_malue)
|
||||
|
||||
self._current_config[interface] = dict(state="enabled", congestion_control=congestion_control,
|
||||
dcb=dcb, if_type=if_type, if_id=if_id)
|
||||
|
||||
def _show_interface_congestion_control(self, if_type, interface):
|
||||
cmd = "show interfaces {0} {1} congestion-control".format(if_type, interface)
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def _show_interface_dcb_ets(self, if_type, interface):
|
||||
cmd = "show dcb ets interface {0} {1}".format(if_type, interface)
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
for interface in self._required_config.get("interfaces"):
|
||||
if_type, if_id = self._get_interface_type(interface)
|
||||
if not if_id:
|
||||
self._module.fail_json(
|
||||
msg='unsupported interface: {0}'.format(interface))
|
||||
interface_congestion_control_config = self._show_interface_congestion_control(if_type, if_id)
|
||||
if interface_congestion_control_config is not None:
|
||||
self._set_interface_congestion_control_config(interface_congestion_control_config,
|
||||
interface, if_type, if_id)
|
||||
else:
|
||||
self._module.fail_json(
|
||||
msg='Interface {0} does not exist on switch'.format(interface))
|
||||
|
||||
def generate_commands(self):
|
||||
state = self._required_config.get("state")
|
||||
tc = self._required_config.get("tc")
|
||||
interfaces = self._required_config.get("interfaces")
|
||||
for interface in interfaces:
|
||||
current_interface = self._current_config.get(interface)
|
||||
current_state = current_interface.get("state")
|
||||
if_type = current_interface.get("if_type")
|
||||
if_id = current_interface.get("if_id")
|
||||
if state == "disabled":
|
||||
if current_state == "enabled":
|
||||
self._commands.append('interface {0} {1} no traffic-class {2} congestion-control'.format(if_type, if_id, tc))
|
||||
continue
|
||||
|
||||
congestion_control = self._required_config.get("congestion_control")
|
||||
|
||||
if congestion_control is not None:
|
||||
control = congestion_control.get("control")
|
||||
current_congestion_control = current_interface.get("congestion_control")
|
||||
threshold_mode = congestion_control.get("threshold_mode")
|
||||
min_threshold = congestion_control.get("min_threshold")
|
||||
max_threshold = congestion_control.get("max_threshold")
|
||||
if current_congestion_control is None:
|
||||
self._threshold_mode_generate_cmds_mappers(threshold_mode, if_type, if_id, tc,
|
||||
control, min_threshold, max_threshold)
|
||||
else:
|
||||
current_control = current_congestion_control.get("control")
|
||||
curr_threshold_mode = current_congestion_control.get("threshold_mode")
|
||||
curr_min_threshold = current_congestion_control.get("min_threshold")
|
||||
curr_max_threshold = current_congestion_control.get("max_threshold")
|
||||
|
||||
if control != current_control:
|
||||
self._threshold_mode_generate_cmds_mappers(threshold_mode, if_type, if_id, tc,
|
||||
control, min_threshold, max_threshold)
|
||||
else:
|
||||
if threshold_mode != curr_threshold_mode:
|
||||
self._threshold_mode_generate_cmds_mappers(threshold_mode, if_type, if_id, tc,
|
||||
control, min_threshold, max_threshold)
|
||||
elif min_threshold != curr_min_threshold or max_threshold != curr_max_threshold:
|
||||
self._threshold_mode_generate_cmds_mappers(threshold_mode, if_type, if_id, tc,
|
||||
control, min_threshold, max_threshold)
|
||||
|
||||
dcb = self._required_config.get("dcb")
|
||||
if dcb is not None:
|
||||
dcb_mode = dcb.get("mode")
|
||||
current_dcb = current_interface.get("dcb")
|
||||
current_dcb_mode = current_dcb.get("mode")
|
||||
if dcb_mode == "strict" and dcb_mode != current_dcb_mode:
|
||||
self._commands.append('interface {0} {1} traffic-class {2} '
|
||||
'dcb ets {3}'.format(if_type, if_id, tc, dcb_mode))
|
||||
elif dcb_mode == "wrr":
|
||||
weight = dcb.get("weight")
|
||||
current_weight = current_dcb.get("weight")
|
||||
if dcb_mode != current_dcb_mode or weight != current_weight:
|
||||
self._commands.append('interface {0} {1} traffic-class {2} '
|
||||
'dcb ets {3} {4}'.format(if_type, if_id, tc, dcb_mode, weight))
|
||||
|
||||
def _threshold_mode_generate_cmds_mappers(self, threshold_mode, if_type, if_id, tc,
|
||||
control, min_threshold, max_threshold):
|
||||
if threshold_mode == 'absolute':
|
||||
self._generate_congestion_control_absolute_cmds(if_type, if_id, tc, control,
|
||||
min_threshold, max_threshold)
|
||||
else:
|
||||
self._generate_congestion_control_relative_cmds(if_type, if_id, tc, control,
|
||||
min_threshold, max_threshold)
|
||||
|
||||
def _generate_congestion_control_absolute_cmds(self, if_type, if_id, tc, control,
|
||||
min_absolute, max_absolute):
|
||||
self._commands.append('interface {0} {1} traffic-class {2} '
|
||||
'congestion-control {3} minimum-absolute {4} '
|
||||
'maximum-absolute {5}'.format(if_type, if_id, tc, control,
|
||||
min_absolute, max_absolute))
|
||||
|
||||
def _generate_congestion_control_relative_cmds(self, if_type, if_id, tc, control,
|
||||
min_relative, max_relative):
|
||||
self._commands.append('interface {0} {1} traffic-class {2} '
|
||||
'congestion-control {3} minimum-relative {4} '
|
||||
'maximum-relative {5}'.format(if_type, if_id, tc, control,
|
||||
min_relative, max_relative))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxTrafficClassModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
289
plugins/modules/network/onyx/onyx_username.py
Normal file
289
plugins/modules/network/onyx/onyx_username.py
Normal file
@@ -0,0 +1,289 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_username
|
||||
author: "Anas Shami (@anass)"
|
||||
short_description: Configure username module
|
||||
description:
|
||||
- This module provides declarative management of users/roles
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
options:
|
||||
username:
|
||||
description:
|
||||
- Create/Edit user using username
|
||||
type: str
|
||||
required: True
|
||||
full_name:
|
||||
description:
|
||||
- Set the full name of this user
|
||||
type: str
|
||||
nopassword:
|
||||
description:
|
||||
- Clear password for such user
|
||||
type: bool
|
||||
default: False
|
||||
password:
|
||||
description:
|
||||
- Set password fot such user
|
||||
type: str
|
||||
encrypted_password:
|
||||
description:
|
||||
- Decide the type of setted password (plain text or encrypted)
|
||||
type: bool
|
||||
default: False
|
||||
capability:
|
||||
description:
|
||||
- Grant capability to this user account
|
||||
type: str
|
||||
choices: ['monitor', 'unpriv', 'v_admin', 'admin']
|
||||
reset_capability:
|
||||
description:
|
||||
- Reset capability to this user account
|
||||
type: bool
|
||||
default: False
|
||||
disconnected:
|
||||
description:
|
||||
- Disconnect all sessions of this user
|
||||
type: bool
|
||||
default: False
|
||||
disabled:
|
||||
description:
|
||||
- Disable means of logging into this account
|
||||
type: str
|
||||
choices: ['none', 'login', 'password', 'all']
|
||||
state:
|
||||
description:
|
||||
- Set state of the given account
|
||||
default: present
|
||||
type: str
|
||||
choices: ['present', 'absent']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: create new user
|
||||
onyx_username:
|
||||
username: anass
|
||||
|
||||
- name: set the user full-name
|
||||
onyx_username:
|
||||
username: anass
|
||||
full_name: anasshami
|
||||
|
||||
- name: set the user encrypted password
|
||||
onyx_username:
|
||||
username: anass
|
||||
password: 12345
|
||||
encrypted_password: True
|
||||
|
||||
- name: set the user capability
|
||||
onyx_username:
|
||||
username: anass
|
||||
capability: monitor
|
||||
|
||||
- name: reset the user capability
|
||||
onyx_username:
|
||||
username: anass
|
||||
reset_capability: True
|
||||
|
||||
- name: remove the user configuration
|
||||
onyx_username:
|
||||
username: anass
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- username *
|
||||
- username * password *
|
||||
- username * nopassword
|
||||
- username * disable login
|
||||
- username * capability admin
|
||||
- no username *
|
||||
- no username * disable
|
||||
|
||||
"""
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule, show_cmd
|
||||
|
||||
|
||||
class OnyxUsernameModule(BaseOnyxModule):
|
||||
ACCOUNT_STATE = {
|
||||
'Account locked out': dict(disabled='all'),
|
||||
'No password required for login': dict(nopassword=True),
|
||||
'Local password login disabled': dict(disabled='password'),
|
||||
'Account disabled': dict(disabled='all')
|
||||
}
|
||||
ENCRYPTED_ID = 7
|
||||
|
||||
def init_module(self):
|
||||
"""
|
||||
module initialization
|
||||
"""
|
||||
element_spec = dict()
|
||||
|
||||
argument_spec = dict(state=dict(choices=['absent', 'present'], default='present'),
|
||||
username=dict(type='str', required=True),
|
||||
disabled=dict(choices=['none', 'login', 'password', 'all']),
|
||||
capability=dict(choices=['monitor', 'unpriv', 'v_admin', 'admin']),
|
||||
nopassword=dict(type='bool', default=False),
|
||||
password=dict(type='str', no_log=True),
|
||||
encrypted_password=dict(type='bool', default=False),
|
||||
reset_capability=dict(type="bool", default=False),
|
||||
disconnected=dict(type='bool', default=False),
|
||||
full_name=dict(type='str'))
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[['password', 'nopassword']])
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = dict()
|
||||
module_params = self._module.params
|
||||
params = {}
|
||||
''' Requred/Default fields '''
|
||||
params['username'] = module_params.get('username')
|
||||
params['state'] = module_params.get('state')
|
||||
params['encrypted_password'] = module_params.get('encrypted_password')
|
||||
params['reset_capability'] = module_params.get('reset_capability')
|
||||
''' Other fields '''
|
||||
for key, value in module_params.items():
|
||||
if value is not None:
|
||||
params[key] = value
|
||||
self.validate_param_values(params)
|
||||
self._required_config = params
|
||||
|
||||
def _get_username_config(self):
|
||||
return show_cmd(self._module, "show usernames", json_fmt=True, fail_on_error=False)
|
||||
|
||||
def _set_current_config(self, users_config):
|
||||
'''
|
||||
users_config ex:
|
||||
{
|
||||
admin": [
|
||||
{
|
||||
"CAPABILITY": "admin",
|
||||
"ACCOUNT STATUS": "No password required for login",
|
||||
"FULL NAME": "System Administrator"
|
||||
}
|
||||
],
|
||||
}
|
||||
'''
|
||||
if not users_config:
|
||||
return
|
||||
current_config = self._current_config
|
||||
for username, config in users_config.items():
|
||||
config_json = config[0]
|
||||
current_config[username] = current_config.get(username, {})
|
||||
account_status = config_json.get('ACCOUNT STATUS')
|
||||
status_value = self.ACCOUNT_STATE.get(account_status)
|
||||
|
||||
if status_value is not None:
|
||||
# None for enabled account with password account "Password set (SHA512 | MD5)" so we won't change any attribute here.
|
||||
current_config[username].update(status_value)
|
||||
current_config[username].update({
|
||||
'capability': config_json.get('CAPABILITY'),
|
||||
'full_name': config_json.get('FULL NAME')
|
||||
})
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
users_config = self._get_username_config()
|
||||
self._set_current_config(users_config)
|
||||
|
||||
def generate_commands(self):
|
||||
current_config, required_config = self._current_config, self._required_config
|
||||
username = required_config.get('username')
|
||||
current_user = current_config.get(username)
|
||||
if current_user is not None:
|
||||
'''created account we just need to edit his attributes'''
|
||||
full_name = required_config.get('full_name')
|
||||
if full_name is not None and current_user.get('full_name') != full_name:
|
||||
self._commands.append("username {0} full-name {1}".format(username, full_name))
|
||||
|
||||
disabled = required_config.get('disabled')
|
||||
if disabled is not None and current_user.get('disabled') != disabled:
|
||||
if disabled == 'none':
|
||||
self._commands.append("no username {0} disable".format(username))
|
||||
elif disabled == 'all':
|
||||
self._commands.append("username {0} disable".format(username))
|
||||
else:
|
||||
self._commands.append("username {0} disable {1}".format(username, disabled))
|
||||
|
||||
state = required_config.get('state')
|
||||
if state == 'absent': # this will remove the user
|
||||
self._commands.append("no username {0}".format(username))
|
||||
|
||||
capability = required_config.get('capability')
|
||||
if capability is not None and current_user.get('capability') != capability:
|
||||
self._commands.append("username {0} capability {1}".format(username, capability))
|
||||
|
||||
reset_capability = required_config.get('reset_capability')
|
||||
if reset_capability:
|
||||
self._commands.append("no username {0} capability".format(username))
|
||||
|
||||
password = required_config.get('password')
|
||||
if password is not None:
|
||||
encrypted = required_config.get('encrypted_password')
|
||||
if encrypted:
|
||||
self._commands.append("username {0} password {1} {2}".format(username, self.ENCRYPTED_ID, password))
|
||||
else:
|
||||
self._commands.append("username {0} password {1}".format(username, password))
|
||||
|
||||
nopassword = required_config.get('nopassword')
|
||||
if nopassword and nopassword != current_user.get('nopassword', False):
|
||||
self._commands.append("username {0} nopassword".format(username))
|
||||
|
||||
disconnected = required_config.get('disconnected')
|
||||
if disconnected:
|
||||
self._commands.append("username {0} disconnect".format(username))
|
||||
else:
|
||||
'''create new account if we have valid inforamtion, just check for username, capability, full_name, password'''
|
||||
|
||||
capability = required_config.get('capability')
|
||||
password = required_config.get('password')
|
||||
full_name = required_config.get('full_name')
|
||||
if capability is not None or password is not None or full_name is not None:
|
||||
if capability is not None:
|
||||
self._commands.append("username {0} capability {1}".format(username, capability))
|
||||
|
||||
if password is not None:
|
||||
encrypted = required_config.get('encrypted_password')
|
||||
if encrypted:
|
||||
self._commands.append("username {0} password {1} {2} ".format(username, self.ENCRYPTED_ID, password))
|
||||
else:
|
||||
self._commands.append("username {0} password {1}".format(username, password))
|
||||
|
||||
if full_name is not None:
|
||||
self._commands.append("username {0} full-name {1}".format(username, full_name))
|
||||
|
||||
else:
|
||||
self._commands.append("username {0}".format(username))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxUsernameModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
204
plugins/modules/network/onyx/onyx_vlan.py
Normal file
204
plugins/modules/network/onyx/onyx_vlan.py
Normal file
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_vlan
|
||||
author: "Samer Deeb (@samerd) Alex Tabachnik (@atabachnik)"
|
||||
short_description: Manage VLANs on Mellanox ONYX network devices
|
||||
description:
|
||||
- This module provides declarative management of VLANs
|
||||
on Mellanox ONYX network devices.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the VLAN.
|
||||
vlan_id:
|
||||
description:
|
||||
- ID of the VLAN.
|
||||
aggregate:
|
||||
description: List of VLANs definitions.
|
||||
purge:
|
||||
description:
|
||||
- Purge VLANs not defined in the I(aggregate) parameter.
|
||||
default: no
|
||||
type: bool
|
||||
state:
|
||||
description:
|
||||
- State of the VLAN configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure VLAN ID and name
|
||||
onyx_vlan:
|
||||
vlan_id: 20
|
||||
name: test-vlan
|
||||
|
||||
- name: remove configuration
|
||||
onyx_vlan:
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device
|
||||
returned: always.
|
||||
type: list
|
||||
sample:
|
||||
- vlan 20
|
||||
- name test-vlan
|
||||
- exit
|
||||
"""
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
|
||||
|
||||
class OnyxVlanModule(BaseOnyxModule):
|
||||
_purge = False
|
||||
|
||||
@classmethod
|
||||
def _get_element_spec(cls):
|
||||
return dict(
|
||||
vlan_id=dict(type='int'),
|
||||
name=dict(type='str'),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_aggregate_spec(cls, element_spec):
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['vlan_id'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
return aggregate_spec
|
||||
|
||||
def init_module(self):
|
||||
""" module initialization
|
||||
"""
|
||||
element_spec = self._get_element_spec()
|
||||
aggregate_spec = self._get_aggregate_spec(element_spec)
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict',
|
||||
options=aggregate_spec),
|
||||
purge=dict(default=False, type='bool'),
|
||||
)
|
||||
argument_spec.update(element_spec)
|
||||
required_one_of = [['vlan_id', 'aggregate']]
|
||||
mutually_exclusive = [['vlan_id', 'aggregate']]
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
def validate_vlan_id(self, value):
|
||||
if value and not 1 <= int(value) <= 4094:
|
||||
self._module.fail_json(msg='vlan id must be between 1 and 4094')
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = list()
|
||||
module_params = self._module.params
|
||||
aggregate = module_params.get('aggregate')
|
||||
self._purge = module_params.get('purge', False)
|
||||
if aggregate:
|
||||
for item in aggregate:
|
||||
for key in item:
|
||||
if item.get(key) is None:
|
||||
item[key] = module_params[key]
|
||||
self.validate_param_values(item, item)
|
||||
req_item = item.copy()
|
||||
req_item['vlan_id'] = int(req_item['vlan_id'])
|
||||
self._required_config.append(req_item)
|
||||
else:
|
||||
params = {
|
||||
'vlan_id': module_params['vlan_id'],
|
||||
'name': module_params['name'],
|
||||
'state': module_params['state'],
|
||||
}
|
||||
self.validate_param_values(params)
|
||||
self._required_config.append(params)
|
||||
|
||||
def _create_vlan_data(self, vlan_id, vlan_data):
|
||||
if self._os_version >= self.ONYX_API_VERSION:
|
||||
vlan_data = vlan_data[0]
|
||||
return {
|
||||
'vlan_id': vlan_id,
|
||||
'name': self.get_config_attr(vlan_data, 'Name')
|
||||
}
|
||||
|
||||
def _get_vlan_config(self):
|
||||
return show_cmd(self._module, "show vlan")
|
||||
|
||||
def load_current_config(self):
|
||||
# called in base class in run function
|
||||
self._os_version = self._get_os_version()
|
||||
self._current_config = dict()
|
||||
vlan_config = self._get_vlan_config()
|
||||
if not vlan_config:
|
||||
return
|
||||
for vlan_id, vlan_data in iteritems(vlan_config):
|
||||
try:
|
||||
vlan_id = int(vlan_id)
|
||||
except ValueError:
|
||||
continue
|
||||
self._current_config[vlan_id] = \
|
||||
self._create_vlan_data(vlan_id, vlan_data)
|
||||
|
||||
def generate_commands(self):
|
||||
req_vlans = set()
|
||||
for req_conf in self._required_config:
|
||||
state = req_conf['state']
|
||||
vlan_id = req_conf['vlan_id']
|
||||
if state == 'absent':
|
||||
if vlan_id in self._current_config:
|
||||
self._commands.append('no vlan %s' % vlan_id)
|
||||
else:
|
||||
req_vlans.add(vlan_id)
|
||||
self._generate_vlan_commands(vlan_id, req_conf)
|
||||
if self._purge:
|
||||
for vlan_id in self._current_config:
|
||||
if vlan_id not in req_vlans:
|
||||
self._commands.append('no vlan %s' % vlan_id)
|
||||
|
||||
def _generate_vlan_commands(self, vlan_id, req_conf):
|
||||
curr_vlan = self._current_config.get(vlan_id, {})
|
||||
if not curr_vlan:
|
||||
self._commands.append("vlan %s" % vlan_id)
|
||||
self._commands.append("exit")
|
||||
req_name = req_conf['name']
|
||||
curr_name = curr_vlan.get('name')
|
||||
if req_name:
|
||||
if req_name != curr_name:
|
||||
self._commands.append("vlan %s name %s" % (vlan_id, req_name))
|
||||
elif req_name is not None:
|
||||
if curr_name:
|
||||
self._commands.append("vlan %s no name" % vlan_id)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxVlanModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
264
plugins/modules/network/onyx/onyx_vxlan.py
Normal file
264
plugins/modules/network/onyx/onyx_vxlan.py
Normal file
@@ -0,0 +1,264 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_vxlan
|
||||
author: "Anas Badaha (@anasb)"
|
||||
short_description: Configures Vxlan
|
||||
description:
|
||||
- This module provides declarative management of Vxlan configuration
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
- Tested on ONYX evpn_dev.031.
|
||||
- nve protocol must be enabled.
|
||||
options:
|
||||
nve_id:
|
||||
description:
|
||||
- nve interface ID.
|
||||
required: true
|
||||
loopback_id:
|
||||
description:
|
||||
- loopback interface ID.
|
||||
bgp:
|
||||
description:
|
||||
- configure bgp on nve interface.
|
||||
type: bool
|
||||
default: true
|
||||
mlag_tunnel_ip:
|
||||
description:
|
||||
- vxlan Mlag tunnel IP
|
||||
vni_vlan_list:
|
||||
description:
|
||||
- Each item in the list has two attributes vlan_id, vni_id.
|
||||
arp_suppression:
|
||||
description:
|
||||
- A flag telling if to configure arp suppression.
|
||||
type: bool
|
||||
default: false
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure Vxlan
|
||||
onyx_vxlan:
|
||||
nve_id: 1
|
||||
loopback_id: 1
|
||||
bgp: yes
|
||||
mlag-tunnel-ip: 100.0.0.1
|
||||
vni_vlan_list:
|
||||
- vlan_id: 10
|
||||
vni_id: 10010
|
||||
- vlan_id: 6
|
||||
vni_id: 10060
|
||||
arp_suppression: yes
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- interface nve 1
|
||||
- interface nve 1 vxlan source interface loopback 1
|
||||
- interface nve 1 nve controller bgp
|
||||
- interface nve 1 vxlan mlag-tunnel-ip 100.0.0.1
|
||||
- interface nve 1 nve vni 10010 vlan 10
|
||||
- interface nve 1 nve vni 10060 vlan 6
|
||||
- interface nve 1 nve neigh-suppression
|
||||
- interface vlan 6
|
||||
- interface vlan 10
|
||||
"""
|
||||
|
||||
import re
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule
|
||||
|
||||
|
||||
class OnyxVxlanModule(BaseOnyxModule):
|
||||
|
||||
LOOPBACK_REGEX = re.compile(r'^loopback (\d+).*')
|
||||
NVE_ID_REGEX = re.compile(r'^Interface NVE (\d+).*')
|
||||
|
||||
def init_module(self):
|
||||
""" initialize module
|
||||
"""
|
||||
vni_vlan_spec = dict(vlan_id=dict(type=int),
|
||||
vni_id=dict(type=int))
|
||||
element_spec = dict(
|
||||
nve_id=dict(type=int),
|
||||
loopback_id=dict(type=int),
|
||||
bgp=dict(default=True, type='bool'),
|
||||
mlag_tunnel_ip=dict(type='str'),
|
||||
vni_vlan_list=dict(type='list',
|
||||
elements='dict',
|
||||
options=vni_vlan_spec),
|
||||
arp_suppression=dict(default=False, type='bool')
|
||||
)
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
def get_required_config(self):
|
||||
module_params = self._module.params
|
||||
self._required_config = dict(module_params)
|
||||
self.validate_param_values(self._required_config)
|
||||
|
||||
def _set_vxlan_config(self, vxlan_config):
|
||||
vxlan_config = vxlan_config[0]
|
||||
if not vxlan_config:
|
||||
return
|
||||
nve_header = vxlan_config.get("header")
|
||||
match = self.NVE_ID_REGEX.match(nve_header)
|
||||
if match:
|
||||
current_nve_id = int(match.group(1))
|
||||
self._current_config['nve_id'] = current_nve_id
|
||||
if int(current_nve_id) != self._required_config.get("nve_id"):
|
||||
return
|
||||
|
||||
self._current_config['mlag_tunnel_ip'] = vxlan_config.get("Mlag tunnel IP")
|
||||
controller_mode = vxlan_config.get("Controller mode")
|
||||
if controller_mode == "BGP":
|
||||
self._current_config['bgp'] = True
|
||||
else:
|
||||
self._current_config['bgp'] = False
|
||||
|
||||
loopback_str = vxlan_config.get("Source interface")
|
||||
match = self.LOOPBACK_REGEX.match(loopback_str)
|
||||
if match:
|
||||
loopback_id = match.group(1)
|
||||
self._current_config['loopback_id'] = int(loopback_id)
|
||||
|
||||
self._current_config['global_neigh_suppression'] = vxlan_config.get("Global Neigh-Suppression")
|
||||
|
||||
vni_vlan_mapping = self._current_config['vni_vlan_mapping'] = dict()
|
||||
nve_detail = self._show_nve_detail()
|
||||
|
||||
if nve_detail is not None:
|
||||
nve_detail = nve_detail[0]
|
||||
|
||||
if nve_detail:
|
||||
for vlan_id in nve_detail:
|
||||
vni_vlan_mapping[int(vlan_id)] = dict(
|
||||
vni_id=int(nve_detail[vlan_id][0].get("VNI")),
|
||||
arp_suppression=nve_detail[vlan_id][0].get("Neigh Suppression"))
|
||||
|
||||
def _show_vxlan_config(self):
|
||||
cmd = "show interfaces nve"
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def _show_nve_detail(self):
|
||||
cmd = "show interface nve {0} detail".format(self._required_config.get("nve_id"))
|
||||
return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)
|
||||
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
vxlan_config = self._show_vxlan_config()
|
||||
if vxlan_config:
|
||||
self._set_vxlan_config(vxlan_config)
|
||||
|
||||
def generate_commands(self):
|
||||
nve_id = self._required_config.get("nve_id")
|
||||
current_nve_id = self._current_config.get("nve_id")
|
||||
|
||||
if current_nve_id is None:
|
||||
self._add_nve_commands(nve_id)
|
||||
elif current_nve_id != nve_id:
|
||||
self._add_no_nve_commands(current_nve_id)
|
||||
self._add_nve_commands(nve_id)
|
||||
|
||||
bgp = self._required_config.get("bgp")
|
||||
if bgp is not None:
|
||||
curr_bgp = self._current_config.get("bgp")
|
||||
if bgp and bgp != curr_bgp:
|
||||
self._commands.append('interface nve {0} nve controller bgp'.format(nve_id))
|
||||
|
||||
loopback_id = self._required_config.get("loopback_id")
|
||||
if loopback_id is not None:
|
||||
curr_loopback_id = self._current_config.get("loopback_id")
|
||||
if loopback_id != curr_loopback_id:
|
||||
self._commands.append('interface nve {0} vxlan source interface '
|
||||
'loopback {1} '.format(nve_id, loopback_id))
|
||||
|
||||
mlag_tunnel_ip = self._required_config.get("mlag_tunnel_ip")
|
||||
if mlag_tunnel_ip is not None:
|
||||
curr_mlag_tunnel_ip = self._current_config.get("mlag_tunnel_ip")
|
||||
if mlag_tunnel_ip != curr_mlag_tunnel_ip:
|
||||
self._commands.append('interface nve {0} vxlan '
|
||||
'mlag-tunnel-ip {1}'.format(nve_id, mlag_tunnel_ip))
|
||||
|
||||
vni_vlan_list = self._required_config.get("vni_vlan_list")
|
||||
arp_suppression = self._required_config.get("arp_suppression")
|
||||
if vni_vlan_list is not None:
|
||||
self._generate_vni_vlan_cmds(vni_vlan_list, nve_id, arp_suppression)
|
||||
|
||||
def _generate_vni_vlan_cmds(self, vni_vlan_list, nve_id, arp_suppression):
|
||||
|
||||
current_global_arp_suppression = self._current_config.get('global_neigh_suppression')
|
||||
if arp_suppression is True and current_global_arp_suppression != "Enable":
|
||||
self._commands.append('interface nve {0} nve neigh-suppression'.format(nve_id))
|
||||
|
||||
current_vni_vlan_mapping = self._current_config.get('vni_vlan_mapping')
|
||||
if current_vni_vlan_mapping is None:
|
||||
for vni_vlan in vni_vlan_list:
|
||||
vlan_id = vni_vlan.get("vlan_id")
|
||||
vni_id = vni_vlan.get("vni_id")
|
||||
self._add_vni_vlan_cmds(nve_id, vni_id, vlan_id)
|
||||
self._add_arp_suppression_cmds(arp_suppression, vlan_id)
|
||||
else:
|
||||
for vni_vlan in vni_vlan_list:
|
||||
vlan_id = vni_vlan.get("vlan_id")
|
||||
vni_id = vni_vlan.get("vni_id")
|
||||
|
||||
currt_vlan_id = current_vni_vlan_mapping.get(vlan_id)
|
||||
|
||||
if currt_vlan_id is None:
|
||||
self._add_vni_vlan_cmds(nve_id, vni_id, vlan_id)
|
||||
self._add_arp_suppression_cmds(arp_suppression, vlan_id)
|
||||
else:
|
||||
current_vni_id = currt_vlan_id.get("vni_id")
|
||||
current_arp_suppression = currt_vlan_id.get("arp_suppression")
|
||||
|
||||
if int(current_vni_id) != vni_id:
|
||||
self._add_vni_vlan_cmds(nve_id, vni_id, vlan_id)
|
||||
|
||||
if current_arp_suppression == "Disable":
|
||||
self._add_arp_suppression_cmds(arp_suppression, vlan_id)
|
||||
|
||||
def _add_no_nve_commands(self, current_nve_id):
|
||||
self._commands.append('no interface nve {0}'.format(current_nve_id))
|
||||
|
||||
def _add_nve_commands(self, nve_id):
|
||||
self._commands.append('interface nve {0}'.format(nve_id))
|
||||
self._commands.append('exit')
|
||||
|
||||
def _add_vni_vlan_cmds(self, nve_id, vni_id, vlan_id):
|
||||
self._commands.append('interface nve {0} nve vni {1} '
|
||||
'vlan {2}'.format(nve_id, vni_id, vlan_id))
|
||||
|
||||
def _add_arp_suppression_cmds(self, arp_suppression, vlan_id):
|
||||
if arp_suppression is True:
|
||||
self._commands.append('interface vlan {0}'.format(vlan_id))
|
||||
self._commands.append('exit')
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxVxlanModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
223
plugins/modules/network/onyx/onyx_wjh.py
Normal file
223
plugins/modules/network/onyx/onyx_wjh.py
Normal file
@@ -0,0 +1,223 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: onyx_wjh
|
||||
author: "Anas Shami (@anass)"
|
||||
short_description: Configure what-just-happend module
|
||||
description:
|
||||
- This module provides declarative management of wjh
|
||||
on Mellanox ONYX network devices.
|
||||
notes:
|
||||
options:
|
||||
group:
|
||||
description:
|
||||
- Name of wjh group.
|
||||
choices: ['all', 'forwarding', 'acl']
|
||||
type: str
|
||||
enabled:
|
||||
description:
|
||||
- wjh group status
|
||||
type: bool
|
||||
auto_export:
|
||||
description:
|
||||
- wjh group auto export pcap file status
|
||||
type: bool
|
||||
export_group:
|
||||
description:
|
||||
- wjh group auto export group
|
||||
choices: ['all', 'forwarding', 'acl']
|
||||
type: str
|
||||
clear_group:
|
||||
description:
|
||||
- clear pcap file by group
|
||||
choices: ['all', 'user', 'auto-export']
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: enable wjh
|
||||
onyx_wjh:
|
||||
group: forwarding
|
||||
enabled: True
|
||||
|
||||
- name: disable wjh
|
||||
onyx_wjh:
|
||||
group: forwarding
|
||||
enabled: False
|
||||
|
||||
- name: enable auto-export
|
||||
onyx_wjh:
|
||||
auto_export: True
|
||||
export_group: forwarding
|
||||
- name: disable auto-export
|
||||
onyx_wjh:
|
||||
auto_export: False
|
||||
export_group: forwarding
|
||||
- name: clear pcap file
|
||||
onyx_wjh:
|
||||
clear_group: auto-export
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- what-just-happend forwarding enable
|
||||
- what-just-happend auto-export forwarding enable
|
||||
- clear what-just-happend pcap-file user
|
||||
"""
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule, show_cmd
|
||||
|
||||
|
||||
class OnyxWJHModule(BaseOnyxModule):
|
||||
WJH_DISABLED_REGX = re.compile(r'^no what-just-happened ([a-z]+) enable.*')
|
||||
WJH_DISABLED_AUTO_EXPORT_REGX = re.compile(r'^no what-just-happened auto-export ([a-z]+) enable.*')
|
||||
|
||||
WJH_CMD_FMT = '{0}what-just-happened {1} enable'
|
||||
WJH_EXPORT_CMD_FMT = '{0}what-just-happened auto-export {1} enable'
|
||||
WJH_CLEAR_CMD_FMT = 'clear what-just-happened pcap-files {0}'
|
||||
|
||||
WJH_GROUPS = ['all', 'forwarding', 'acl']
|
||||
CLEAR_GROUPS = ['all', 'user', 'auto-export']
|
||||
|
||||
def init_module(self):
|
||||
"""
|
||||
module initialization
|
||||
"""
|
||||
element_spec = dict(group=dict(choices=self.WJH_GROUPS),
|
||||
enabled=dict(type='bool'),
|
||||
auto_export=dict(type='bool'),
|
||||
export_group=dict(choices=self.WJH_GROUPS),
|
||||
clear_group=dict(choices=self.CLEAR_GROUPS))
|
||||
|
||||
argument_spec = dict()
|
||||
argument_spec.update(element_spec)
|
||||
self._module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_together=[
|
||||
['group', 'enabled'],
|
||||
['auto_export', 'export_group']
|
||||
])
|
||||
|
||||
def get_required_config(self):
|
||||
self._required_config = dict()
|
||||
module_params = self._module.params
|
||||
group = module_params.get('group')
|
||||
export_group = module_params.get('export_group')
|
||||
clear_group = module_params.get('clear_group')
|
||||
|
||||
params = dict()
|
||||
if group:
|
||||
enabled = module_params.get('enabled')
|
||||
params.update({
|
||||
'group': group,
|
||||
'enabled': enabled
|
||||
})
|
||||
|
||||
if export_group:
|
||||
auto_export = module_params.get('auto_export')
|
||||
params.update({
|
||||
'export_group': export_group,
|
||||
'auto_export': auto_export
|
||||
})
|
||||
|
||||
if clear_group:
|
||||
params.update({
|
||||
'clear_group': clear_group
|
||||
})
|
||||
|
||||
self.validate_param_values(params)
|
||||
self._required_config = params
|
||||
|
||||
def _get_wjh_config(self):
|
||||
return show_cmd(self._module, "show running-config | include .*what-just-happened.*", json_fmt=False, fail_on_error=False)
|
||||
|
||||
def _set_current_config(self, config):
|
||||
if not config:
|
||||
return
|
||||
current_config = self._current_config
|
||||
lines = config.split('\n')
|
||||
for line in lines:
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
match = self.WJH_DISABLED_REGX.match(line)
|
||||
if match:
|
||||
# wjh is disabled
|
||||
group = match.group(1)
|
||||
current_config[group] = False
|
||||
|
||||
match = self.WJH_DISABLED_AUTO_EXPORT_REGX.match(line)
|
||||
if match:
|
||||
# wjh auto export is disabled
|
||||
export_group = match.group(1) + '_export'
|
||||
current_config[export_group] = False
|
||||
|
||||
'''
|
||||
show running config will contains [no wjh * group enable] if disabled - default config is enabled
|
||||
'''
|
||||
def load_current_config(self):
|
||||
self._current_config = dict()
|
||||
config_lines = self._get_wjh_config()
|
||||
if config_lines:
|
||||
self._set_current_config(config_lines)
|
||||
|
||||
def wjh_group_status(self, current_config, group_value, suffix=''):
|
||||
current_enabled = False
|
||||
if group_value == 'all':
|
||||
# no disabled group so all would be false
|
||||
current_enabled = not all([
|
||||
(group + suffix) in current_config for group in self.WJH_GROUPS])
|
||||
else:
|
||||
# if no current-value its enabled
|
||||
current_enabled = current_config[group_value + suffix] if((group_value + suffix) in current_config) else True
|
||||
return current_enabled
|
||||
|
||||
'''
|
||||
wjh is enabled "by default"
|
||||
when wjh disable we will find no wjh commands in running config
|
||||
'''
|
||||
def generate_commands(self):
|
||||
current_config, required_config = self._current_config, self._required_config
|
||||
group = required_config.get('group')
|
||||
export_group = required_config.get('export_group')
|
||||
clear_group = required_config.get('clear_group')
|
||||
if group:
|
||||
current_enabled = self.wjh_group_status(current_config, group)
|
||||
if(required_config['enabled'] != current_enabled):
|
||||
self._commands.append(self.WJH_CMD_FMT
|
||||
.format(('' if required_config['enabled'] else 'no '), group))
|
||||
if export_group:
|
||||
current_enabled = self.wjh_group_status(current_config, required_config['export_group'], '_export')
|
||||
if(required_config['auto_export'] != current_enabled):
|
||||
self._commands.append(self.WJH_EXPORT_CMD_FMT
|
||||
.format(('' if required_config['auto_export'] else 'no '), export_group))
|
||||
if clear_group:
|
||||
# clear pcap files
|
||||
self._commands.append(self.WJH_CLEAR_CMD_FMT.format(clear_group))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
OnyxWJHModule.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user