1#!/usr/local/bin/python3.8 2# 3# Copyright: Ansible Project 4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 6from __future__ import absolute_import, division, print_function 7__metaclass__ = type 8 9DOCUMENTATION = ''' 10--- 11module: onyx_lldp_interface 12author: "Samer Deeb (@samerd)" 13short_description: Manage LLDP interfaces configuration on Mellanox ONYX network devices 14description: 15 - This module provides declarative management of LLDP interfaces 16 configuration on Mellanox ONYX network devices. 17options: 18 name: 19 description: 20 - Name of the interface LLDP should be configured on. 21 aggregate: 22 description: List of interfaces LLDP should be configured on. 23 purge: 24 description: 25 - Purge interfaces not defined in the aggregate parameter. 26 type: bool 27 default: false 28 state: 29 description: 30 - State of the LLDP configuration. 31 default: present 32 choices: ['present', 'absent', 'enabled', 'disabled'] 33''' 34 35EXAMPLES = """ 36- name: Configure LLDP on specific interfaces 37 onyx_lldp_interface: 38 name: Eth1/1 39 state: present 40 41- name: Disable LLDP on specific interfaces 42 onyx_lldp_interface: 43 name: Eth1/1 44 state: disabled 45 46- name: Enable LLDP on specific interfaces 47 onyx_lldp_interface: 48 name: Eth1/1 49 state: enabled 50 51- name: Delete LLDP on specific interfaces 52 onyx_lldp_interface: 53 name: Eth1/1 54 state: absent 55 56- name: Create aggregate of LLDP interface configurations 57 onyx_lldp_interface: 58 aggregate: 59 - { name: Eth1/1 } 60 - { name: Eth1/2 } 61 state: present 62 63- name: Delete aggregate of LLDP interface configurations 64 onyx_lldp_interface: 65 aggregate: 66 - { name: Eth1/1 } 67 - { name: Eth1/2 } 68 state: absent 69""" 70 71RETURN = """ 72commands: 73 description: The list of configuration mode commands to send to the device 74 returned: always. 75 type: list 76 sample: 77 - interface ethernet 1/1 lldp transmit 78 - interface ethernet 1/1 lldp receive 79""" 80import re 81from copy import deepcopy 82 83from ansible.module_utils.basic import AnsibleModule 84from ansible.module_utils.six import iteritems 85from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec 86 87from ansible_collections.mellanox.onyx.plugins.module_utils.network.onyx.onyx import BaseOnyxModule 88from ansible_collections.mellanox.onyx.plugins.module_utils.network.onyx.onyx import show_cmd 89 90 91class OnyxLldpInterfaceModule(BaseOnyxModule): 92 IF_NAME_REGEX = re.compile(r"^(Eth\d+\/\d+|Eth\d+\/\d+\d+)$") 93 _purge = False 94 95 @classmethod 96 def _get_element_spec(cls): 97 return dict( 98 name=dict(type='str'), 99 state=dict(default='present', 100 choices=['present', 'absent', 'enabled', 'disabled']), 101 ) 102 103 @classmethod 104 def _get_aggregate_spec(cls, element_spec): 105 aggregate_spec = deepcopy(element_spec) 106 aggregate_spec['name'] = dict(required=True) 107 108 # remove default in aggregate spec, to handle common arguments 109 remove_default_spec(aggregate_spec) 110 return aggregate_spec 111 112 def init_module(self): 113 """ module initialization 114 """ 115 element_spec = self._get_element_spec() 116 aggregate_spec = self._get_aggregate_spec(element_spec) 117 argument_spec = dict( 118 aggregate=dict(type='list', elements='dict', 119 options=aggregate_spec), 120 purge=dict(default=False, type='bool'), 121 ) 122 argument_spec.update(element_spec) 123 required_one_of = [['name', 'aggregate']] 124 mutually_exclusive = [['name', 'aggregate']] 125 self._module = AnsibleModule( 126 argument_spec=argument_spec, 127 required_one_of=required_one_of, 128 mutually_exclusive=mutually_exclusive, 129 supports_check_mode=True) 130 131 def get_required_config(self): 132 self._required_config = list() 133 module_params = self._module.params 134 aggregate = module_params.get('aggregate') 135 self._purge = module_params.get('purge', False) 136 if aggregate: 137 for item in aggregate: 138 for key in item: 139 if item.get(key) is None: 140 item[key] = module_params[key] 141 self.validate_param_values(item, item) 142 req_item = item.copy() 143 self._required_config.append(req_item) 144 else: 145 params = { 146 'name': module_params['name'], 147 'state': module_params['state'], 148 } 149 self.validate_param_values(params) 150 self._required_config.append(params) 151 152 def _create_if_lldp_data(self, if_name, if_lldp_data): 153 return { 154 'name': if_name, 155 'receive': self.get_config_attr(if_lldp_data, 'Receive'), 156 'transmit': self.get_config_attr(if_lldp_data, 'Transmit'), 157 } 158 159 def _get_lldp_config(self): 160 return show_cmd(self._module, "show lldp interfaces") 161 162 def load_current_config(self): 163 # called in base class in run function 164 self._current_config = dict() 165 lldp_config = self._get_lldp_config() 166 if not lldp_config: 167 return 168 for if_name, if_lldp_data in iteritems(lldp_config): 169 match = self.IF_NAME_REGEX.match(if_name) 170 if not match: 171 continue 172 if if_lldp_data: 173 if_lldp_data = if_lldp_data[0] 174 self._current_config[if_name] = \ 175 self._create_if_lldp_data(if_name, if_lldp_data) 176 177 def _get_interface_cmd_name(self, if_name): 178 return if_name.replace("Eth", "ethernet ") 179 180 def _add_if_lldp_commands(self, if_name, flag, enable): 181 cmd_prefix = "interface %s " % self._get_interface_cmd_name(if_name) 182 lldp_cmd = "lldp %s" % flag 183 if not enable: 184 lldp_cmd = 'no %s' % lldp_cmd 185 self._commands.append(cmd_prefix + lldp_cmd) 186 187 def _gen_lldp_commands(self, if_name, req_state, curr_conf): 188 curr_receive = curr_conf.get('receive') 189 curr_transmit = curr_conf.get('transmit') 190 enable = (req_state == 'Enabled') 191 if curr_receive != req_state: 192 flag = 'receive' 193 self._add_if_lldp_commands(if_name, flag, enable) 194 if curr_transmit != req_state: 195 flag = 'transmit' 196 self._add_if_lldp_commands(if_name, flag, enable) 197 198 def generate_commands(self): 199 req_interfaces = set() 200 for req_conf in self._required_config: 201 state = req_conf['state'] 202 if_name = req_conf['name'] 203 if state in ('absent', 'disabled'): 204 req_state = 'Disabled' 205 else: 206 req_interfaces.add(if_name) 207 req_state = 'Enabled' 208 curr_conf = self._current_config.get(if_name, {}) 209 self._gen_lldp_commands(if_name, req_state, curr_conf) 210 if self._purge: 211 for if_name, curr_conf in iteritems(self._current_config): 212 if if_name not in req_interfaces: 213 req_state = 'Disabled' 214 self._gen_lldp_commands(if_name, req_state, curr_conf) 215 216 217def main(): 218 """ main entry point for module execution 219 """ 220 OnyxLldpInterfaceModule.main() 221 222 223if __name__ == '__main__': 224 main() 225