ansible-later/env_27/lib/python2.7/site-packages/ansible/modules/network/nxos/_nxos_portchannel.py
2019-04-11 13:00:36 +02:00

481 lines
15 KiB
Python

#!/usr/bin/python
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'network'}
DOCUMENTATION = '''
---
module: nxos_portchannel
extends_documentation_fragment: nxos
version_added: "2.2"
deprecated:
removed_in: "2.9"
why: Replaced with common C(*_linkagg) network modules.
alternative: Use M(nxos_linkagg) instead.
short_description: Manages port-channel interfaces.
description:
- Manages port-channel specific configuration parameters.
author:
- Jason Edelman (@jedelman8)
- Gabriele Gerbino (@GGabriele)
notes:
- Tested against NXOSv 7.3.(0)D1(1) on VIRL
- C(state=absent) removes the portchannel config and interface if it
already exists. If members to be removed are not explicitly
passed, all existing members (if any), are removed.
- Members must be a list.
- LACP needs to be enabled first if active/passive modes are used.
options:
group:
description:
- Channel-group number for the port-channel.
required: true
mode:
description:
- Mode for the port-channel, i.e. on, active, passive.
default: on
choices: ['active','passive','on']
min_links:
description:
- Min links required to keep portchannel up.
members:
description:
- List of interfaces that will be managed in a given portchannel.
force:
description:
- When true it forces port-channel members to match what is
declared in the members param. This can be used to remove
members.
choices: [ 'false', 'true' ]
default: 'false'
state:
description:
- Manage the state of the resource.
default: present
choices: ['present','absent']
'''
EXAMPLES = '''
# Ensure port-channel99 is created, add two members, and set to mode on
- nxos_portchannel:
group: 99
members: ['Ethernet1/1','Ethernet1/2']
mode: 'active'
state: present
'''
RETURN = '''
commands:
description: command sent to the device
returned: always
type: list
sample: ["interface Ethernet2/6", "no channel-group 12",
"interface Ethernet2/5", "no channel-group 12",
"interface Ethernet2/6", "channel-group 12 mode on",
"interface Ethernet2/5", "channel-group 12 mode on"]
'''
import collections
import re
from ansible.module_utils.network.nxos.nxos import get_config, load_config, run_commands
from ansible.module_utils.network.nxos.nxos import get_capabilities, nxos_argument_spec
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.config import CustomNetworkConfig
def get_value(arg, config, module):
param_to_command_keymap = {
'min_links': 'lacp min-links'
}
REGEX = re.compile(r'(?:{0}\s)(?P<value>.*)$'.format(param_to_command_keymap[arg]), re.M)
value = ''
if param_to_command_keymap[arg] in config:
value = REGEX.search(config).group('value')
return value
def check_interface(module, netcfg):
config = str(netcfg)
REGEX = re.compile(r'\s+interface port-channel{0}$'.format(module.params['group']), re.M)
value = False
try:
if REGEX.search(config):
value = True
except TypeError:
value = False
return value
def get_custom_value(arg, config, module):
REGEX = re.compile(r'\s+member vni {0} associate-vrf\s*$'.format(
module.params['vni']), re.M)
value = False
try:
if REGEX.search(config):
value = True
except TypeError:
value = False
return value
def get_portchannel_members(pchannel):
try:
members = pchannel['TABLE_member']['ROW_member']
except KeyError:
members = []
return members
def get_portchannel_mode(interface, protocol, module, netcfg):
if protocol != 'LACP':
mode = 'on'
else:
netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
parents = ['interface {0}'.format(interface.capitalize())]
body = netcfg.get_section(parents)
mode_list = body.split('\n')
for line in mode_list:
this_line = line.strip()
if this_line.startswith('channel-group'):
find = this_line
if 'mode' in find:
if 'passive' in find:
mode = 'passive'
elif 'active' in find:
mode = 'active'
return mode
def get_portchannel(module, netcfg=None):
command = 'show port-channel summary | json'
portchannel = {}
portchannel_table = {}
members = []
try:
body = run_commands(module, [command])[0]
pc_table = body['TABLE_channel']['ROW_channel']
if isinstance(pc_table, dict):
pc_table = [pc_table]
for pc in pc_table:
if pc['group'] == module.params['group']:
portchannel_table = pc
elif module.params['group'].isdigit() and pc['group'] == int(module.params['group']):
portchannel_table = pc
except (KeyError, AttributeError, TypeError, IndexError):
return {}
if portchannel_table:
portchannel['group'] = portchannel_table['group']
protocol = portchannel_table['prtcl']
members_list = get_portchannel_members(portchannel_table)
if isinstance(members_list, dict):
members_list = [members_list]
member_dictionary = {}
for each_member in members_list:
interface = each_member['port']
members.append(interface)
pc_member = {}
pc_member['status'] = str(each_member['port-status'])
pc_member['mode'] = get_portchannel_mode(interface,
protocol, module, netcfg)
member_dictionary[interface] = pc_member
portchannel['members'] = members
portchannel['members_detail'] = member_dictionary
# Ensure each member have the same mode.
modes = set()
for each, value in member_dictionary.items():
modes.update([value['mode']])
if len(modes) == 1:
portchannel['mode'] = value['mode']
else:
portchannel['mode'] = 'unknown'
return portchannel
def get_existing(module, args):
existing = {}
netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
interface_exist = check_interface(module, netcfg)
if interface_exist:
parents = ['interface port-channel{0}'.format(module.params['group'])]
config = netcfg.get_section(parents)
if config:
existing['min_links'] = get_value('min_links', config, module)
existing.update(get_portchannel(module, netcfg=netcfg))
return existing, interface_exist
def config_portchannel(proposed, mode, group, force):
commands = []
# NOTE: Leading whitespace for force option is important
force = ' force' if force else ''
config_args = {
'mode': 'channel-group {group}{force} mode {mode}',
'min_links': 'lacp min-links {min_links}',
}
for member in proposed.get('members', []):
commands.append('interface {0}'.format(member))
commands.append(config_args.get('mode').format(group=group, force=force, mode=mode))
min_links = proposed.get('min_links', None)
if min_links:
command = 'interface port-channel {0}'.format(group)
commands.append(command)
commands.append(config_args.get('min_links').format(
min_links=min_links))
return commands
def get_commands_to_add_members(proposed, existing, force, module):
try:
proposed_members = proposed['members']
except KeyError:
proposed_members = []
try:
existing_members = existing['members']
except KeyError:
existing_members = []
members_to_add = list(set(proposed_members).difference(existing_members))
commands = []
# NOTE: Leading whitespace for force option is important
force = ' force' if force else ''
if members_to_add:
for member in members_to_add:
commands.append('interface {0}'.format(member))
commands.append('channel-group {0}{1} mode {2}'.format(
existing['group'], force, proposed['mode']))
return commands
def get_commands_to_remove_members(proposed, existing, module):
try:
proposed_members = proposed['members']
except KeyError:
proposed_members = []
try:
existing_members = existing['members']
except KeyError:
existing_members = []
members_to_remove = list(set(existing_members).difference(proposed_members))
commands = []
if members_to_remove:
for member in members_to_remove:
commands.append('interface {0}'.format(member))
commands.append('no channel-group {0}'.format(existing['group']))
return commands
def get_commands_if_mode_change(proposed, existing, group, mode, force, module):
try:
proposed_members = proposed['members']
except KeyError:
proposed_members = []
try:
existing_members = existing['members']
except KeyError:
existing_members = []
try:
members_dict = existing['members_detail']
except KeyError:
members_dict = {}
members_to_remove = set(existing_members).difference(proposed_members)
members_with_mode_change = []
if members_dict:
for interface, values in members_dict.items():
if (interface in proposed_members and
(interface not in members_to_remove)):
if values['mode'] != mode:
members_with_mode_change.append(interface)
commands = []
# NOTE: Leading whitespace for force option is important
force = ' force' if force else ''
if members_with_mode_change:
for member in members_with_mode_change:
commands.append('interface {0}'.format(member))
commands.append('no channel-group {0}'.format(group))
for member in members_with_mode_change:
commands.append('interface {0}'.format(member))
commands.append('channel-group {0}{1} mode {2}'.format(group, force, mode))
return commands
def get_commands_min_links(existing, proposed, group, min_links, module):
commands = []
try:
if (existing['min_links'] is None or
(existing['min_links'] != proposed['min_links'])):
commands.append('interface port-channel{0}'.format(group))
commands.append('lacp min-link {0}'.format(min_links))
except KeyError:
commands.append('interface port-channel{0}'.format(group))
commands.append('lacp min-link {0}'.format(min_links))
return commands
def flatten_list(command_lists):
flat_command_list = []
for command in command_lists:
if isinstance(command, list):
flat_command_list.extend(command)
else:
flat_command_list.append(command)
return flat_command_list
def state_present(module, existing, proposed, interface_exist, force, warnings):
commands = []
group = str(module.params['group'])
mode = module.params['mode']
min_links = module.params['min_links']
if not interface_exist:
command = config_portchannel(proposed, mode, group, force)
commands.append(command)
commands.insert(0, 'interface port-channel{0}'.format(group))
warnings.append("The proposed port-channel interface did not "
"exist. It's recommended to use nxos_interface to "
"create all logical interfaces.")
elif existing and interface_exist:
if force:
command = get_commands_to_remove_members(proposed, existing, module)
commands.append(command)
command = get_commands_to_add_members(proposed, existing, force, module)
commands.append(command)
mode_command = get_commands_if_mode_change(proposed, existing, group, mode, force, module)
commands.insert(0, mode_command)
if min_links:
command = get_commands_min_links(existing, proposed, group, min_links, module)
commands.append(command)
return commands
def state_absent(module, existing, proposed):
commands = []
group = str(module.params['group'])
commands.append(['no interface port-channel{0}'.format(group)])
return commands
def main():
argument_spec = dict(
group=dict(required=True, type='str'),
mode=dict(required=False, choices=['on', 'active', 'passive'], default='on', type='str'),
min_links=dict(required=False, default=None, type='str'),
members=dict(required=False, default=None, type='list'),
force=dict(required=False, default='false', type='str', choices=['true', 'false']),
state=dict(required=False, choices=['absent', 'present'], default='present'),
)
argument_spec.update(nxos_argument_spec)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
warnings = list()
results = dict(changed=False, warnings=warnings)
group = str(module.params['group'])
mode = module.params['mode']
min_links = module.params['min_links']
members = module.params['members']
state = module.params['state']
if str(module.params['force']).lower() == 'true':
force = True
elif module.params['force'] == 'false':
force = False
if ((min_links or mode) and
(not members and state == 'present')):
module.fail_json(msg='"members" is required when state=present and '
'"min_links" or "mode" are provided')
args = [
'group',
'members',
'min_links',
'mode'
]
existing, interface_exist = get_existing(module, args)
proposed = dict((k, v) for k, v in module.params.items()
if v is not None and k in args)
commands = []
if state == 'absent' and existing:
commands = state_absent(module, existing, proposed)
elif state == 'present':
commands = state_present(module, existing, proposed, interface_exist, force, warnings)
cmds = flatten_list(commands)
if cmds:
if module.check_mode:
module.exit_json(**results)
else:
load_config(module, cmds)
results['changed'] = True
if 'configure' in cmds:
cmds.pop(0)
results['commands'] = cmds
module.exit_json(**results)
if __name__ == '__main__':
main()