mirror of https://github.com/ansible/ansible.git
VMware: New module vmware_dvswitch_lacp (#48555)
parent
fb7b6f9521
commit
3f6878ac84
@ -0,0 +1,403 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright: (c) 2018, Christian Kotte <christian.kotte@gmx.de>
|
||||||
|
#
|
||||||
|
# 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: vmware_dvswitch_lacp
|
||||||
|
short_description: Manage LACP configuration on a Distributed Switch
|
||||||
|
description:
|
||||||
|
- This module can be used to configure Link Aggregation Control Protocol (LACP) support mode and Link Aggregation Groups (LAGs).
|
||||||
|
version_added: 2.8
|
||||||
|
author:
|
||||||
|
- Christian Kotte (@ckotte)
|
||||||
|
notes:
|
||||||
|
- Tested on vSphere 6.7
|
||||||
|
- You need to run the task two times if you want to remove all LAGs and change the support mode to 'basic'
|
||||||
|
requirements:
|
||||||
|
- "python >= 2.6"
|
||||||
|
- PyVmomi
|
||||||
|
options:
|
||||||
|
switch:
|
||||||
|
description:
|
||||||
|
- The name of the Distributed Switch to manage.
|
||||||
|
required: True
|
||||||
|
aliases: ['dvswitch']
|
||||||
|
support_mode:
|
||||||
|
description:
|
||||||
|
- The LACP support mode.
|
||||||
|
- 'C(basic): One Link Aggregation Control Protocol group in the switch (singleLag).'
|
||||||
|
- 'C(enhanced): Multiple Link Aggregation Control Protocol groups in the switch (multipleLag).'
|
||||||
|
type: str
|
||||||
|
default: 'basic'
|
||||||
|
choices: ['basic', 'enhanced']
|
||||||
|
link_aggregation_groups:
|
||||||
|
description:
|
||||||
|
- Can only be used if C(lacp_support) is set to C(enhanced).
|
||||||
|
- 'The following parameters are required:'
|
||||||
|
- '- C(name) (string): Name of the LAG.'
|
||||||
|
- '- C(uplink_number) (int): Number of uplinks. Can 1 to 30.'
|
||||||
|
- '- C(mode) (string): The negotiating state of the uplinks/ports.'
|
||||||
|
- ' - choices: [ active, passive ]'
|
||||||
|
- '- C(load_balancing_mode) (string): Load balancing algorithm.'
|
||||||
|
- ' - Valid attributes are:'
|
||||||
|
- ' - srcTcpUdpPort: Source TCP/UDP port number.'
|
||||||
|
- ' - srcDestIpTcpUdpPortVlan: Source and destination IP, source and destination TCP/UDP port number and VLAN.'
|
||||||
|
- ' - srcIpVlan: Source IP and VLAN.'
|
||||||
|
- ' - srcDestTcpUdpPort: Source and destination TCP/UDP port number.'
|
||||||
|
- ' - srcMac: Source MAC address.'
|
||||||
|
- ' - destIp: Destination IP.'
|
||||||
|
- ' - destMac: Destination MAC address.'
|
||||||
|
- ' - vlan: VLAN only.'
|
||||||
|
- ' - srcDestIp: Source and Destination IP.'
|
||||||
|
- ' - srcIpTcpUdpPortVlan: Source IP, TCP/UDP port number and VLAN.'
|
||||||
|
- ' - srcDestIpTcpUdpPort: Source and destination IP and TCP/UDP port number.'
|
||||||
|
- ' - srcDestMac: Source and destination MAC address.'
|
||||||
|
- ' - destIpTcpUdpPort: Destination IP and TCP/UDP port number.'
|
||||||
|
- ' - srcPortId: Source Virtual Port Id.'
|
||||||
|
- ' - srcIp: Source IP.'
|
||||||
|
- ' - srcIpTcpUdpPort: Source IP and TCP/UDP port number.'
|
||||||
|
- ' - destIpTcpUdpPortVlan: Destination IP, TCP/UDP port number and VLAN.'
|
||||||
|
- ' - destTcpUdpPort: Destination TCP/UDP port number.'
|
||||||
|
- ' - destIpVlan: Destination IP and VLAN.'
|
||||||
|
- ' - srcDestIpVlan: Source and destination IP and VLAN.'
|
||||||
|
- ' - The default load balancing mode in the vSphere Client is srcDestIpTcpUdpPortVlan.'
|
||||||
|
- Please see examples for more information.
|
||||||
|
type: list
|
||||||
|
extends_documentation_fragment: vmware.documentation
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = '''
|
||||||
|
- name: Enable enhanced mode on a Distributed Switch
|
||||||
|
vmware_dvswitch_lacp:
|
||||||
|
hostname: '{{ inventory_hostname }}'
|
||||||
|
username: '{{ vcenter_username }}'
|
||||||
|
password: '{{ vcenter_password }}'
|
||||||
|
switch: dvSwitch
|
||||||
|
support_mode: enhanced
|
||||||
|
validate_certs: "{{ validate_vcenter_certs }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
|
with_items: "{{ vcenter_distributed_switches }}"
|
||||||
|
|
||||||
|
- name: Enable enhanced mode and create two LAGs on a Distributed Switch
|
||||||
|
vmware_dvswitch_lacp:
|
||||||
|
hostname: '{{ inventory_hostname }}'
|
||||||
|
username: '{{ vcenter_username }}'
|
||||||
|
password: '{{ vcenter_password }}'
|
||||||
|
switch: dvSwitch
|
||||||
|
support_mode: enhanced
|
||||||
|
link_aggregation_groups:
|
||||||
|
- name: lag1
|
||||||
|
uplink_number: 2
|
||||||
|
mode: active
|
||||||
|
load_balancing_mode: srcDestIpTcpUdpPortVlan
|
||||||
|
- name: lag2
|
||||||
|
uplink_number: 2
|
||||||
|
mode: passive
|
||||||
|
load_balancing_mode: srcDestIp
|
||||||
|
validate_certs: "{{ validate_vcenter_certs }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
|
with_items: "{{ vcenter_distributed_switches }}"
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = """
|
||||||
|
result:
|
||||||
|
description: information about performed operation
|
||||||
|
returned: always
|
||||||
|
type: string
|
||||||
|
sample: {
|
||||||
|
"changed": true,
|
||||||
|
"dvswitch": "dvSwitch",
|
||||||
|
"link_aggregation_groups": [
|
||||||
|
{"load_balancing_mode": "srcDestIpTcpUdpPortVlan", "mode": "active", "name": "lag1", "uplink_number": 2},
|
||||||
|
{"load_balancing_mode": "srcDestIp", "mode": "active", "name": "lag2", "uplink_number": 2}
|
||||||
|
],
|
||||||
|
"link_aggregation_groups_previous": [],
|
||||||
|
"support_mode": "enhanced",
|
||||||
|
"result": "lacp lags changed"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
from pyVmomi import vim, vmodl
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils._text import to_native
|
||||||
|
from ansible.module_utils.vmware import (
|
||||||
|
PyVmomi, TaskError, find_dvs_by_name, vmware_argument_spec, wait_for_task
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class VMwareDvSwitchLacp(PyVmomi):
|
||||||
|
"""Class to manage a LACP on a Distributed Virtual Switch"""
|
||||||
|
def __init__(self, module):
|
||||||
|
super(VMwareDvSwitchLacp, self).__init__(module)
|
||||||
|
self.switch_name = self.module.params['switch']
|
||||||
|
self.support_mode = self.module.params['support_mode']
|
||||||
|
self.link_aggregation_groups = self.module.params['link_aggregation_groups']
|
||||||
|
if self.support_mode == 'basic' and (
|
||||||
|
self.link_aggregation_groups and not (
|
||||||
|
len(self.link_aggregation_groups) == 1 and self.link_aggregation_groups[0] == '')):
|
||||||
|
self.module.fail_json(
|
||||||
|
msg="LAGs can only be configured if 'support_mode' is set to 'enhanced'!"
|
||||||
|
)
|
||||||
|
self.dvs = find_dvs_by_name(self.content, self.switch_name)
|
||||||
|
if self.dvs is None:
|
||||||
|
self.module.fail_json(msg="Failed to find DVS %s" % self.switch_name)
|
||||||
|
|
||||||
|
def ensure(self):
|
||||||
|
"""Manage LACP configuration"""
|
||||||
|
changed = changed_support_mode = changed_lags = False
|
||||||
|
results = dict(changed=changed)
|
||||||
|
results['dvswitch'] = self.switch_name
|
||||||
|
changed_list = []
|
||||||
|
|
||||||
|
spec = vim.dvs.VmwareDistributedVirtualSwitch.ConfigSpec()
|
||||||
|
spec.configVersion = self.dvs.config.configVersion
|
||||||
|
|
||||||
|
# Check support mode
|
||||||
|
results['support_mode'] = self.support_mode
|
||||||
|
lacp_support_mode = self.get_lacp_support_mode(self.support_mode)
|
||||||
|
if self.dvs.config.lacpApiVersion != lacp_support_mode:
|
||||||
|
changed = changed_support_mode = True
|
||||||
|
changed_list.append("support mode")
|
||||||
|
results['support_mode_previous'] = self.get_lacp_support_mode(self.dvs.config.lacpApiVersion)
|
||||||
|
spec.lacpApiVersion = lacp_support_mode
|
||||||
|
|
||||||
|
# Check LAGs
|
||||||
|
results['link_aggregation_groups'] = self.link_aggregation_groups
|
||||||
|
if self.link_aggregation_groups and not (
|
||||||
|
len(self.link_aggregation_groups) == 1 and self.link_aggregation_groups[0] == ''):
|
||||||
|
if self.dvs.config.lacpGroupConfig:
|
||||||
|
lacp_lag_list = []
|
||||||
|
# Check if desired LAGs are configured
|
||||||
|
for lag in self.link_aggregation_groups:
|
||||||
|
lag_name, lag_mode, lag_uplink_number, lag_load_balancing_mode = self.get_lacp_lag_options(lag)
|
||||||
|
lag_found = False
|
||||||
|
for lacp_group in self.dvs.config.lacpGroupConfig:
|
||||||
|
if lacp_group.name == lag_name:
|
||||||
|
lag_found = True
|
||||||
|
if (lag_mode != lacp_group.mode or
|
||||||
|
lag_uplink_number != lacp_group.uplinkNum or
|
||||||
|
lag_load_balancing_mode != lacp_group.loadbalanceAlgorithm):
|
||||||
|
changed = changed_lags = True
|
||||||
|
lacp_lag_list.append(
|
||||||
|
self.create_lacp_group_spec(
|
||||||
|
'edit',
|
||||||
|
lacp_group.key, lag_name, lag_uplink_number, lag_mode, lag_load_balancing_mode
|
||||||
|
)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
if lag_found is False:
|
||||||
|
changed = changed_lags = True
|
||||||
|
lacp_lag_list.append(
|
||||||
|
self.create_lacp_group_spec(
|
||||||
|
'add', None, lag_name, lag_uplink_number, lag_mode, lag_load_balancing_mode
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# Check if LAGs need to be removed
|
||||||
|
for lacp_group in self.dvs.config.lacpGroupConfig:
|
||||||
|
lag_found = False
|
||||||
|
for lag in self.link_aggregation_groups:
|
||||||
|
result = self.get_lacp_lag_options(lag)
|
||||||
|
if lacp_group.name == result[0]:
|
||||||
|
lag_found = True
|
||||||
|
break
|
||||||
|
if lag_found is False:
|
||||||
|
changed = changed_lags = True
|
||||||
|
lacp_lag_list.append(
|
||||||
|
self.create_lacp_group_spec('remove', lacp_group.key, lacp_group.name, None, None, None)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
changed = changed_lags = True
|
||||||
|
lacp_lag_list = []
|
||||||
|
for lag in self.link_aggregation_groups:
|
||||||
|
lag_name, lag_mode, lag_uplink_number, lag_load_balancing_mode = self.get_lacp_lag_options(lag)
|
||||||
|
lacp_lag_list.append(
|
||||||
|
self.create_lacp_group_spec(
|
||||||
|
'add', None, lag_name, lag_uplink_number, lag_mode, lag_load_balancing_mode
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if self.dvs.config.lacpGroupConfig:
|
||||||
|
changed = changed_lags = True
|
||||||
|
lacp_lag_list = []
|
||||||
|
for lacp_group in self.dvs.config.lacpGroupConfig:
|
||||||
|
lacp_lag_list.append(
|
||||||
|
self.create_lacp_group_spec('remove', lacp_group.key, lacp_group.name, None, None, None)
|
||||||
|
)
|
||||||
|
if changed_lags:
|
||||||
|
changed_list.append("link aggregation groups")
|
||||||
|
current_lags_list = []
|
||||||
|
for lacp_group in self.dvs.config.lacpGroupConfig:
|
||||||
|
temp_lag = dict()
|
||||||
|
temp_lag['name'] = lacp_group.name
|
||||||
|
temp_lag['uplink_number'] = lacp_group.uplinkNum
|
||||||
|
temp_lag['mode'] = lacp_group.mode
|
||||||
|
temp_lag['load_balancing_mode'] = lacp_group.loadbalanceAlgorithm
|
||||||
|
current_lags_list.append(temp_lag)
|
||||||
|
results['link_aggregation_groups_previous'] = current_lags_list
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
if self.module.check_mode:
|
||||||
|
changed_suffix = ' would be changed'
|
||||||
|
else:
|
||||||
|
changed_suffix = ' changed'
|
||||||
|
if len(changed_list) > 2:
|
||||||
|
message = ', '.join(changed_list[:-1]) + ', and ' + str(changed_list[-1])
|
||||||
|
elif len(changed_list) == 2:
|
||||||
|
message = ' and '.join(changed_list)
|
||||||
|
elif len(changed_list) == 1:
|
||||||
|
message = changed_list[0]
|
||||||
|
message += changed_suffix
|
||||||
|
if not self.module.check_mode:
|
||||||
|
if changed_support_mode and self.support_mode == 'basic' and changed_lags:
|
||||||
|
self.update_lacp_group_config(self.dvs, lacp_lag_list)
|
||||||
|
# NOTE: You need to run the task again to change the support mode to 'basic' as well
|
||||||
|
# No matter how long you sleep, you will always get the following error in vCenter:
|
||||||
|
# 'Cannot complete operation due to concurrent modification by another operation.'
|
||||||
|
# self.update_dvs_config(self.dvs, spec)
|
||||||
|
else:
|
||||||
|
if changed_support_mode:
|
||||||
|
self.update_dvs_config(self.dvs, spec)
|
||||||
|
if changed_lags:
|
||||||
|
self.update_lacp_group_config(self.dvs, lacp_lag_list)
|
||||||
|
else:
|
||||||
|
message = "LACP already configured properly"
|
||||||
|
results['changed'] = changed
|
||||||
|
results['result'] = message
|
||||||
|
|
||||||
|
self.module.exit_json(**results)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_lacp_support_mode(mode):
|
||||||
|
"""Get LACP support mode"""
|
||||||
|
return_mode = None
|
||||||
|
if mode == 'basic':
|
||||||
|
return_mode = 'singleLag'
|
||||||
|
elif mode == 'enhanced':
|
||||||
|
return_mode = 'multipleLag'
|
||||||
|
elif mode == 'singleLag':
|
||||||
|
return_mode = 'basic'
|
||||||
|
elif mode == 'multipleLag':
|
||||||
|
return_mode = 'enhanced'
|
||||||
|
return return_mode
|
||||||
|
|
||||||
|
def get_lacp_lag_options(self, lag):
|
||||||
|
"""Get and check LACP LAG options"""
|
||||||
|
lag_name = lag.get('name', None)
|
||||||
|
if lag_name is None:
|
||||||
|
self.module.fail_json(msg="Please specify name in lag options as it's a required parameter")
|
||||||
|
lag_mode = lag.get('mode', None)
|
||||||
|
if lag_mode is None:
|
||||||
|
self.module.fail_json(msg="Please specify mode in lag options as it's a required parameter")
|
||||||
|
lag_uplink_number = lag.get('uplink_number', None)
|
||||||
|
if lag_uplink_number is None:
|
||||||
|
self.module.fail_json(msg="Please specify uplink_number in lag options as it's a required parameter")
|
||||||
|
elif lag_uplink_number > 30:
|
||||||
|
self.module.fail_json(msg="More than 30 uplinks are not supported in a single LAG!")
|
||||||
|
lag_load_balancing_mode = lag.get('load_balancing_mode', None)
|
||||||
|
supported_lb_modes = ['srcTcpUdpPort', 'srcDestIpTcpUdpPortVlan', 'srcIpVlan', 'srcDestTcpUdpPort',
|
||||||
|
'srcMac', 'destIp', 'destMac', 'vlan', 'srcDestIp', 'srcIpTcpUdpPortVlan',
|
||||||
|
'srcDestIpTcpUdpPort', 'srcDestMac', 'destIpTcpUdpPort', 'srcPortId', 'srcIp',
|
||||||
|
'srcIpTcpUdpPort', 'destIpTcpUdpPortVlan', 'destTcpUdpPort', 'destIpVlan', 'srcDestIpVlan']
|
||||||
|
if lag_load_balancing_mode is None:
|
||||||
|
self.module.fail_json(msg="Please specify load_balancing_mode in lag options as it's a required parameter")
|
||||||
|
elif lag_load_balancing_mode not in supported_lb_modes:
|
||||||
|
self.module.fail_json(msg="The specified load balancing mode '%s' isn't supported!" % lag_load_balancing_mode)
|
||||||
|
return lag_name, lag_mode, lag_uplink_number, lag_load_balancing_mode
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_lacp_group_spec(operation, key, name, uplink_number, mode, load_balancing_mode):
|
||||||
|
"""
|
||||||
|
Create LACP group spec
|
||||||
|
operation: add, edit, or remove
|
||||||
|
Returns: LACP group spec
|
||||||
|
"""
|
||||||
|
lacp_spec = vim.dvs.VmwareDistributedVirtualSwitch.LacpGroupSpec()
|
||||||
|
lacp_spec.operation = operation
|
||||||
|
lacp_spec.lacpGroupConfig = vim.dvs.VmwareDistributedVirtualSwitch.LacpGroupConfig()
|
||||||
|
lacp_spec.lacpGroupConfig.name = name
|
||||||
|
if operation in ('edit', 'remove'):
|
||||||
|
lacp_spec.lacpGroupConfig.key = key
|
||||||
|
if not operation == 'remove':
|
||||||
|
lacp_spec.lacpGroupConfig.uplinkNum = uplink_number
|
||||||
|
lacp_spec.lacpGroupConfig.mode = mode
|
||||||
|
lacp_spec.lacpGroupConfig.loadbalanceAlgorithm = load_balancing_mode
|
||||||
|
# greyed out in vSphere Client!?
|
||||||
|
# lacp_spec.vlan = vim.dvs.VmwareDistributedVirtualSwitch.LagVlanConfig()
|
||||||
|
# lacp_spec.vlan.vlanId = [vim.NumericRange(...)]
|
||||||
|
# lacp_spec.ipfix = vim.dvs.VmwareDistributedVirtualSwitch.LagIpfixConfig()
|
||||||
|
# lacp_spec.ipfix.ipfixEnabled = True/False
|
||||||
|
return lacp_spec
|
||||||
|
|
||||||
|
def update_dvs_config(self, switch_object, spec):
|
||||||
|
"""Update DVS config"""
|
||||||
|
try:
|
||||||
|
task = switch_object.ReconfigureDvs_Task(spec)
|
||||||
|
result = wait_for_task(task)
|
||||||
|
except TaskError as invalid_argument:
|
||||||
|
self.module.fail_json(
|
||||||
|
msg="Failed to update DVS : %s" % to_native(invalid_argument)
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def update_lacp_group_config(self, switch_object, lacp_group_spec):
|
||||||
|
"""Update LACP group config"""
|
||||||
|
try:
|
||||||
|
task = switch_object.UpdateDVSLacpGroupConfig_Task(lacpGroupSpec=lacp_group_spec)
|
||||||
|
result = wait_for_task(task)
|
||||||
|
except vim.fault.DvsFault as dvs_fault:
|
||||||
|
self.module.fail_json(msg="Update failed due to DVS fault : %s" % to_native(dvs_fault))
|
||||||
|
except vmodl.fault.NotSupported as not_supported:
|
||||||
|
self.module.fail_json(
|
||||||
|
msg="Multiple Link Aggregation Control Protocol groups not supported on the switch : %s" %
|
||||||
|
to_native(not_supported)
|
||||||
|
)
|
||||||
|
except TaskError as invalid_argument:
|
||||||
|
self.module.fail_json(
|
||||||
|
msg="Failed to update Link Aggregation Group : %s" % to_native(invalid_argument)
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main"""
|
||||||
|
argument_spec = vmware_argument_spec()
|
||||||
|
argument_spec.update(
|
||||||
|
dict(
|
||||||
|
switch=dict(required=True, aliases=['dvswitch']),
|
||||||
|
support_mode=dict(default='basic', choices=['basic', 'enhanced']),
|
||||||
|
link_aggregation_groups=dict(default=[], type='list'),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
module = AnsibleModule(
|
||||||
|
argument_spec=argument_spec,
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
vmware_dvswitch_lacp = VMwareDvSwitchLacp(module)
|
||||||
|
vmware_dvswitch_lacp.ensure()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue