1122 lines
46 KiB
Python
1122 lines
46 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright: (c) 2015, Joseph Callen <jcallen () csc.com>
|
|
# Copyright: (c) 2017-18, Ansible Project
|
|
# Copyright: (c) 2017-18, Abhijeet Kasurde <akasurde@redhat.com>
|
|
# 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_vmkernel
|
|
short_description: Manages a VMware VMkernel Adapter of an ESXi host.
|
|
description:
|
|
- This module can be used to manage the VMKernel adapters / VMKernel network interfaces of an ESXi host.
|
|
- The module assumes that the host is already configured with the Port Group in case of a vSphere Standard Switch (vSS).
|
|
- The module assumes that the host is already configured with the Distributed Port Group in case of a vSphere Distributed Switch (vDS).
|
|
- The module automatically migrates the VMKernel adapter from vSS to vDS or vice versa if present.
|
|
version_added: 2.0
|
|
author:
|
|
- Joseph Callen (@jcpowermac)
|
|
- Russell Teague (@mtnbikenc)
|
|
- Abhijeet Kasurde (@Akasurde)
|
|
- Christian Kotte (@ckotte)
|
|
notes:
|
|
- The option C(device) need to be used with DHCP because otherwise it's not possible to check if a VMkernel device is already present
|
|
- You can only change from DHCP to static, and vSS to vDS, or vice versa, in one step, without creating a new device, with C(device) specified.
|
|
- You can only create the VMKernel adapter on a vDS if authenticated to vCenter and not if authenticated to ESXi.
|
|
- Tested on vSphere 5.5 and 6.5
|
|
requirements:
|
|
- "python >= 2.6"
|
|
- PyVmomi
|
|
options:
|
|
vswitch_name:
|
|
description:
|
|
- The name of the vSwitch where to add the VMKernel interface.
|
|
- Required parameter only if C(state) is set to C(present).
|
|
- Optional parameter from version 2.5 and onwards.
|
|
type: str
|
|
aliases: ['vswitch']
|
|
dvswitch_name:
|
|
description:
|
|
- The name of the vSphere Distributed Switch (vDS) where to add the VMKernel interface.
|
|
- Required parameter only if C(state) is set to C(present).
|
|
- Optional parameter from version 2.8 and onwards.
|
|
type: str
|
|
aliases: ['dvswitch']
|
|
version_added: 2.8
|
|
portgroup_name:
|
|
description:
|
|
- The name of the port group for the VMKernel interface.
|
|
required: True
|
|
aliases: ['portgroup']
|
|
network:
|
|
description:
|
|
- A dictionary of network details.
|
|
- 'The following parameter is required:'
|
|
- ' - C(type) (string): Type of IP assignment (either C(dhcp) or C(static)).'
|
|
- 'The following parameters are required in case of C(type) is set to C(static):'
|
|
- ' - C(ip_address) (string): Static IP address (implies C(type: static)).'
|
|
- ' - C(subnet_mask) (string): Static netmask required for C(ip_address).'
|
|
- 'The following parameter is optional in case of C(type) is set to C(static):'
|
|
- ' - C(default_gateway) (string): Default gateway (Override default gateway for this adapter).'
|
|
- 'The following parameter is optional:'
|
|
- ' - C(tcpip_stack) (string): The TCP/IP stack for the VMKernel interface. Can be default, provisioning, vmotion, or vxlan. (default: default)'
|
|
type: dict
|
|
default: {
|
|
type: 'static',
|
|
tcpip_stack: 'default',
|
|
}
|
|
version_added: 2.5
|
|
ip_address:
|
|
description:
|
|
- The IP Address for the VMKernel interface.
|
|
- Use C(network) parameter with C(ip_address) instead.
|
|
- Deprecated option, will be removed in version 2.9.
|
|
subnet_mask:
|
|
description:
|
|
- The Subnet Mask for the VMKernel interface.
|
|
- Use C(network) parameter with C(subnet_mask) instead.
|
|
- Deprecated option, will be removed in version 2.9.
|
|
mtu:
|
|
description:
|
|
- The MTU for the VMKernel interface.
|
|
- The default value of 1500 is valid from version 2.5 and onwards.
|
|
default: 1500
|
|
device:
|
|
description:
|
|
- Search VMkernel adapter by device name.
|
|
- The parameter is required only in case of C(type) is set to C(dhcp).
|
|
version_added: 2.8
|
|
enable_vsan:
|
|
description:
|
|
- Enable VSAN traffic on the VMKernel adapter.
|
|
- This option is only allowed if the default TCP/IP stack is used.
|
|
type: bool
|
|
enable_vmotion:
|
|
description:
|
|
- Enable vMotion traffic on the VMKernel adapter.
|
|
- This option is only allowed if the default TCP/IP stack is used.
|
|
- You cannot enable vMotion on an additional adapter if you already have an adapter with the vMotion TCP/IP stack configured.
|
|
type: bool
|
|
enable_mgmt:
|
|
description:
|
|
- Enable Management traffic on the VMKernel adapter.
|
|
- This option is only allowed if the default TCP/IP stack is used.
|
|
type: bool
|
|
enable_ft:
|
|
description:
|
|
- Enable Fault Tolerance traffic on the VMKernel adapter.
|
|
- This option is only allowed if the default TCP/IP stack is used.
|
|
type: bool
|
|
enable_provisioning:
|
|
description:
|
|
- Enable Provisioning traffic on the VMKernel adapter.
|
|
- This option is only allowed if the default TCP/IP stack is used.
|
|
type: bool
|
|
version_added: 2.8
|
|
enable_replication:
|
|
description:
|
|
- Enable vSphere Replication traffic on the VMKernel adapter.
|
|
- This option is only allowed if the default TCP/IP stack is used.
|
|
type: bool
|
|
version_added: 2.8
|
|
enable_replication_nfc:
|
|
description:
|
|
- Enable vSphere Replication NFC traffic on the VMKernel adapter.
|
|
- This option is only allowed if the default TCP/IP stack is used.
|
|
type: bool
|
|
version_added: 2.8
|
|
state:
|
|
description:
|
|
- If set to C(present), the VMKernel adapter will be created with the given specifications.
|
|
- If set to C(absent), the VMKernel adapter will be removed.
|
|
- If set to C(present) and VMKernel adapter exists, the configurations will be updated.
|
|
choices: [ present, absent ]
|
|
default: present
|
|
version_added: 2.5
|
|
esxi_hostname:
|
|
description:
|
|
- Name of ESXi host to which VMKernel is to be managed.
|
|
- "From version 2.5 onwards, this parameter is required."
|
|
required: True
|
|
version_added: 2.5
|
|
extends_documentation_fragment: vmware.documentation
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
- name: Add Management vmkernel port using static network type
|
|
vmware_vmkernel:
|
|
hostname: '{{ esxi_hostname }}'
|
|
username: '{{ esxi_username }}'
|
|
password: '{{ esxi_password }}'
|
|
esxi_hostname: '{{ esxi_hostname }}'
|
|
vswitch_name: vSwitch0
|
|
portgroup_name: PG_0001
|
|
network:
|
|
type: 'static'
|
|
ip_address: 192.168.127.10
|
|
subnet_mask: 255.255.255.0
|
|
state: present
|
|
enable_mgmt: True
|
|
delegate_to: localhost
|
|
|
|
- name: Add Management vmkernel port using DHCP network type
|
|
vmware_vmkernel:
|
|
hostname: '{{ esxi_hostname }}'
|
|
username: '{{ esxi_username }}'
|
|
password: '{{ esxi_password }}'
|
|
esxi_hostname: '{{ esxi_hostname }}'
|
|
vswitch_name: vSwitch0
|
|
portgroup_name: PG_0002
|
|
state: present
|
|
network:
|
|
type: 'dhcp'
|
|
enable_mgmt: True
|
|
delegate_to: localhost
|
|
|
|
- name: Change IP allocation from static to dhcp
|
|
vmware_vmkernel:
|
|
hostname: '{{ esxi_hostname }}'
|
|
username: '{{ esxi_username }}'
|
|
password: '{{ esxi_password }}'
|
|
esxi_hostname: '{{ esxi_hostname }}'
|
|
vswitch_name: vSwitch0
|
|
portgroup_name: PG_0002
|
|
state: present
|
|
device: vmk1
|
|
network:
|
|
type: 'dhcp'
|
|
enable_mgmt: True
|
|
delegate_to: localhost
|
|
|
|
- name: Delete VMkernel port
|
|
vmware_vmkernel:
|
|
hostname: '{{ esxi_hostname }}'
|
|
username: '{{ esxi_username }}'
|
|
password: '{{ esxi_password }}'
|
|
esxi_hostname: '{{ esxi_hostname }}'
|
|
vswitch_name: vSwitch0
|
|
portgroup_name: PG_0002
|
|
state: absent
|
|
delegate_to: localhost
|
|
|
|
- name: Add Management vmkernel port to Distributed Switch
|
|
vmware_vmkernel:
|
|
hostname: '{{ vcenter_hostname }}'
|
|
username: '{{ vcenter_username }}'
|
|
password: '{{ vcenter_password }}'
|
|
esxi_hostname: '{{ esxi_hostname }}'
|
|
dvswitch_name: dvSwitch1
|
|
portgroup_name: dvPG_0001
|
|
network:
|
|
type: 'static'
|
|
ip_address: 192.168.127.10
|
|
subnet_mask: 255.255.255.0
|
|
state: present
|
|
enable_mgmt: True
|
|
delegate_to: localhost
|
|
|
|
- name: Add vMotion vmkernel port with vMotion TCP/IP stack
|
|
vmware_vmkernel:
|
|
hostname: '{{ vcenter_hostname }}'
|
|
username: '{{ vcenter_username }}'
|
|
password: '{{ vcenter_password }}'
|
|
esxi_hostname: '{{ esxi_hostname }}'
|
|
dvswitch_name: dvSwitch1
|
|
portgroup_name: dvPG_0001
|
|
network:
|
|
type: 'static'
|
|
ip_address: 192.168.127.10
|
|
subnet_mask: 255.255.255.0
|
|
tcpip_stack: vmotion
|
|
state: present
|
|
delegate_to: localhost
|
|
'''
|
|
|
|
RETURN = r'''
|
|
result:
|
|
description: metadata about VMKernel name
|
|
returned: always
|
|
type: dict
|
|
sample: {
|
|
"changed": false,
|
|
"msg": "VMkernel Adapter already configured properly",
|
|
"device": "vmk1",
|
|
"ipv4": "static",
|
|
"ipv4_gw": "No override",
|
|
"ipv4_ip": "192.168.1.15",
|
|
"ipv4_sm": "255.255.255.0",
|
|
"mtu": 9000,
|
|
"services": "vMotion",
|
|
"switch": "vDS"
|
|
}
|
|
'''
|
|
|
|
try:
|
|
from pyVmomi import vim, vmodl
|
|
except ImportError:
|
|
pass
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from ansible.module_utils.vmware import (
|
|
PyVmomi, TaskError, vmware_argument_spec, wait_for_task,
|
|
find_dvspg_by_name, find_dvs_by_name, get_all_objs
|
|
)
|
|
from ansible.module_utils._text import to_native
|
|
|
|
|
|
class PyVmomiHelper(PyVmomi):
|
|
"""Class to manage VMkernel configuration of an ESXi host system"""
|
|
|
|
def __init__(self, module):
|
|
super(PyVmomiHelper, self).__init__(module)
|
|
if self.params['network']:
|
|
self.network_type = self.params['network'].get('type')
|
|
self.ip_address = self.params['network'].get('ip_address', None)
|
|
self.subnet_mask = self.params['network'].get('subnet_mask', None)
|
|
self.default_gateway = self.params['network'].get('default_gateway', None)
|
|
self.tcpip_stack = self.params['network'].get('tcpip_stack')
|
|
self.device = self.params['device']
|
|
if self.network_type == 'dhcp' and not self.device:
|
|
module.fail_json(msg="device is a required parameter when network type is set to 'dhcp'")
|
|
self.mtu = self.params['mtu']
|
|
self.enable_vsan = self.params['enable_vsan']
|
|
self.enable_vmotion = self.params['enable_vmotion']
|
|
self.enable_mgmt = self.params['enable_mgmt']
|
|
self.enable_ft = self.params['enable_ft']
|
|
self.enable_provisioning = self.params['enable_provisioning']
|
|
self.enable_replication = self.params['enable_replication']
|
|
self.enable_replication_nfc = self.params['enable_replication_nfc']
|
|
|
|
self.vswitch_name = self.params['vswitch_name']
|
|
self.vds_name = self.params['dvswitch_name']
|
|
self.port_group_name = self.params['portgroup_name']
|
|
|
|
self.esxi_host_name = self.params['esxi_hostname']
|
|
hosts = self.get_all_host_objs(esxi_host_name=self.esxi_host_name)
|
|
if hosts:
|
|
self.esxi_host_obj = hosts[0]
|
|
else:
|
|
self.module.fail_json(
|
|
msg="Failed to get details of ESXi server. Please specify esxi_hostname."
|
|
)
|
|
|
|
if self.network_type == 'static':
|
|
if self.module.params['state'] == 'absent':
|
|
pass
|
|
elif not self.ip_address:
|
|
module.fail_json(msg="ip_address is a required parameter when network type is set to 'static'")
|
|
elif not self.subnet_mask:
|
|
module.fail_json(msg="subnet_mask is a required parameter when network type is set to 'static'")
|
|
|
|
# find Port Group
|
|
if self.vswitch_name:
|
|
self.port_group_obj = self.get_port_group_by_name(
|
|
host_system=self.esxi_host_obj,
|
|
portgroup_name=self.port_group_name,
|
|
vswitch_name=self.vswitch_name
|
|
)
|
|
if not self.port_group_obj:
|
|
module.fail_json(msg="Portgroup '%s' not found on vSS '%s'" % (self.port_group_name, self.vswitch_name))
|
|
elif self.vds_name:
|
|
self.dv_switch_obj = find_dvs_by_name(self.content, self.vds_name)
|
|
if not self.dv_switch_obj:
|
|
module.fail_json(msg="vDS '%s' not found" % self.vds_name)
|
|
self.port_group_obj = find_dvspg_by_name(self.dv_switch_obj, self.port_group_name)
|
|
if not self.port_group_obj:
|
|
module.fail_json(msg="Portgroup '%s' not found on vDS '%s'" % (self.port_group_name, self.vds_name))
|
|
|
|
# find VMkernel Adapter
|
|
if self.device:
|
|
self.vnic = self.get_vmkernel_by_device(device_name=self.device)
|
|
else:
|
|
# config change (e.g. DHCP to static, or vice versa); doesn't work with virtual port change
|
|
self.vnic = self.get_vmkernel_by_portgroup_new(port_group_name=self.port_group_name)
|
|
if not self.vnic:
|
|
if self.network_type == 'static':
|
|
# vDS to vSS or vSS to vSS (static IP)
|
|
self.vnic = self.get_vmkernel_by_ip(ip_address=self.ip_address)
|
|
elif self.network_type == 'dhcp':
|
|
# vDS to vSS or vSS to vSS (DHCP)
|
|
self.vnic = self.get_vmkernel_by_device(device_name=self.device)
|
|
|
|
def get_port_group_by_name(self, host_system, portgroup_name, vswitch_name):
|
|
"""
|
|
Get specific port group by given name
|
|
Args:
|
|
host_system: Name of Host System
|
|
portgroup_name: Name of Port Group
|
|
vswitch_name: Name of the vSwitch
|
|
|
|
Returns: List of port groups by given specifications
|
|
|
|
"""
|
|
portgroups = self.get_all_port_groups_by_host(host_system=host_system)
|
|
|
|
for portgroup in portgroups:
|
|
if portgroup.spec.vswitchName == vswitch_name and portgroup.spec.name == portgroup_name:
|
|
return portgroup
|
|
return None
|
|
|
|
def ensure(self):
|
|
"""
|
|
Manage internal VMKernel management
|
|
Returns: NA
|
|
|
|
"""
|
|
host_vmk_states = {
|
|
'absent': {
|
|
'present': self.host_vmk_delete,
|
|
'absent': self.host_vmk_unchange,
|
|
},
|
|
'present': {
|
|
'present': self.host_vmk_update,
|
|
'absent': self.host_vmk_create,
|
|
}
|
|
}
|
|
|
|
try:
|
|
host_vmk_states[self.module.params['state']][self.check_state()]()
|
|
except vmodl.RuntimeFault as runtime_fault:
|
|
self.module.fail_json(msg=to_native(runtime_fault.msg))
|
|
except vmodl.MethodFault as method_fault:
|
|
self.module.fail_json(msg=to_native(method_fault.msg))
|
|
|
|
def get_vmkernel_by_portgroup_new(self, port_group_name=None):
|
|
"""
|
|
Check if vmkernel available or not
|
|
Args:
|
|
port_group_name: name of port group
|
|
|
|
Returns: vmkernel managed object if vmkernel found, false if not
|
|
|
|
"""
|
|
for vnic in self.esxi_host_obj.config.network.vnic:
|
|
# check if it's a vSS Port Group
|
|
if vnic.spec.portgroup == port_group_name:
|
|
return vnic
|
|
# check if it's a vDS Port Group
|
|
try:
|
|
if vnic.spec.distributedVirtualPort.portgroupKey == self.port_group_obj.key:
|
|
return vnic
|
|
except AttributeError:
|
|
pass
|
|
return False
|
|
|
|
def get_vmkernel_by_ip(self, ip_address):
|
|
"""
|
|
Check if vmkernel available or not
|
|
Args:
|
|
ip_address: IP address of vmkernel device
|
|
|
|
Returns: vmkernel managed object if vmkernel found, false if not
|
|
|
|
"""
|
|
vnics = [vnic for vnic in self.esxi_host_obj.config.network.vnic if vnic.spec.ip.ipAddress == ip_address]
|
|
if vnics:
|
|
return vnics[0]
|
|
return None
|
|
|
|
def get_vmkernel_by_device(self, device_name):
|
|
"""
|
|
Check if vmkernel available or not
|
|
Args:
|
|
device_name: name of vmkernel device
|
|
|
|
Returns: vmkernel managed object if vmkernel found, false if not
|
|
|
|
"""
|
|
vnics = [vnic for vnic in self.esxi_host_obj.config.network.vnic if vnic.device == device_name]
|
|
if vnics:
|
|
return vnics[0]
|
|
return None
|
|
|
|
def check_state(self):
|
|
"""
|
|
Check internal state management
|
|
Returns: Present if found and absent if not found
|
|
|
|
"""
|
|
state = 'absent'
|
|
if self.vnic:
|
|
state = 'present'
|
|
|
|
return state
|
|
|
|
def host_vmk_delete(self):
|
|
"""
|
|
Delete VMKernel
|
|
Returns: NA
|
|
|
|
"""
|
|
results = dict(changed=False, msg='')
|
|
vmk_device = self.vnic.device
|
|
try:
|
|
if self.module.check_mode:
|
|
results['msg'] = "VMkernel Adapter would be deleted"
|
|
else:
|
|
self.esxi_host_obj.configManager.networkSystem.RemoveVirtualNic(vmk_device)
|
|
results['msg'] = "VMkernel Adapter deleted"
|
|
results['changed'] = True
|
|
results['device'] = vmk_device
|
|
except vim.fault.NotFound as not_found:
|
|
self.module.fail_json(
|
|
msg="Failed to find vmk to delete due to %s" %
|
|
to_native(not_found.msg)
|
|
)
|
|
except vim.fault.HostConfigFault as host_config_fault:
|
|
self.module.fail_json(
|
|
msg="Failed to delete vmk due host config issues : %s" %
|
|
to_native(host_config_fault.msg)
|
|
)
|
|
|
|
self.module.exit_json(**results)
|
|
|
|
def host_vmk_unchange(self):
|
|
"""
|
|
Denote no change in VMKernel
|
|
Returns: NA
|
|
|
|
"""
|
|
self.module.exit_json(changed=False)
|
|
|
|
def host_vmk_update(self):
|
|
"""
|
|
Function to update VMKernel with given parameters
|
|
Returns: NA
|
|
|
|
"""
|
|
changed = changed_settings = changed_vds = changed_services = \
|
|
changed_service_vmotion = changed_service_mgmt = changed_service_ft = \
|
|
changed_service_vsan = changed_service_prov = changed_service_rep = changed_service_rep_nfc = False
|
|
changed_list = []
|
|
results = dict(changed=False, msg='')
|
|
|
|
results['tcpip_stack'] = self.tcpip_stack
|
|
net_stack_instance_key = self.get_api_net_stack_instance(self.tcpip_stack)
|
|
if self.vnic.spec.netStackInstanceKey != net_stack_instance_key:
|
|
self.module.fail_json(msg="The TCP/IP stack cannot be changed on an existing VMkernel adapter!")
|
|
|
|
# Check MTU
|
|
results['mtu'] = self.mtu
|
|
if self.vnic.spec.mtu != self.mtu:
|
|
changed_settings = True
|
|
changed_list.append("MTU")
|
|
results['mtu_previous'] = self.vnic.spec.mtu
|
|
|
|
# Check IPv4 settings
|
|
results['ipv4'] = self.network_type
|
|
results['ipv4_ip'] = self.ip_address
|
|
results['ipv4_sm'] = self.subnet_mask
|
|
if self.default_gateway:
|
|
results['ipv4_gw'] = self.default_gateway
|
|
else:
|
|
results['ipv4_gw'] = "No override"
|
|
if self.vnic.spec.ip.dhcp:
|
|
if self.network_type == 'static':
|
|
changed_settings = True
|
|
changed_list.append("IPv4 settings")
|
|
results['ipv4_previous'] = "DHCP"
|
|
if not self.vnic.spec.ip.dhcp:
|
|
if self.network_type == 'dhcp':
|
|
changed_settings = True
|
|
changed_list.append("IPv4 settings")
|
|
results['ipv4_previous'] = "static"
|
|
elif self.network_type == 'static':
|
|
if self.ip_address != self.vnic.spec.ip.ipAddress:
|
|
changed_settings = True
|
|
changed_list.append("IP")
|
|
results['ipv4_ip_previous'] = self.vnic.spec.ip.ipAddress
|
|
if self.subnet_mask != self.vnic.spec.ip.subnetMask:
|
|
changed_settings = True
|
|
changed_list.append("SM")
|
|
results['ipv4_sm_previous'] = self.vnic.spec.ip.subnetMask
|
|
if self.default_gateway:
|
|
try:
|
|
if self.default_gateway != self.vnic.spec.ipRouteSpec.ipRouteConfig.defaultGateway:
|
|
changed_settings = True
|
|
changed_list.append("GW override")
|
|
results['ipv4_gw_previous'] = self.vnic.spec.ipRouteSpec.ipRouteConfig.defaultGateway
|
|
except AttributeError:
|
|
changed_settings = True
|
|
changed_list.append("GW override")
|
|
results['ipv4_gw_previous'] = "No override"
|
|
else:
|
|
try:
|
|
if self.vnic.spec.ipRouteSpec.ipRouteConfig.defaultGateway:
|
|
changed_settings = True
|
|
changed_list.append("GW override")
|
|
results['ipv4_gw_previous'] = self.vnic.spec.ipRouteSpec.ipRouteConfig.defaultGateway
|
|
except AttributeError:
|
|
pass
|
|
|
|
# Check virtual port (vSS or vDS)
|
|
results['portgroup'] = self.port_group_name
|
|
dvs_uuid = None
|
|
if self.vswitch_name:
|
|
results['switch'] = self.vswitch_name
|
|
try:
|
|
if self.vnic.spec.distributedVirtualPort.switchUuid:
|
|
changed_vds = True
|
|
changed_list.append("Virtual Port")
|
|
dvs_uuid = self.vnic.spec.distributedVirtualPort.switchUuid
|
|
except AttributeError:
|
|
pass
|
|
if changed_vds:
|
|
results['switch_previous'] = self.find_dvs_by_uuid(dvs_uuid)
|
|
self.dv_switch_obj = find_dvs_by_name(self.content, results['switch_previous'])
|
|
results['portgroup_previous'] = self.find_dvspg_by_key(
|
|
self.dv_switch_obj, self.vnic.spec.distributedVirtualPort.portgroupKey
|
|
)
|
|
elif self.vds_name:
|
|
results['switch'] = self.vds_name
|
|
try:
|
|
if self.vnic.spec.distributedVirtualPort.switchUuid != self.dv_switch_obj.uuid:
|
|
changed_vds = True
|
|
changed_list.append("Virtual Port")
|
|
dvs_uuid = self.vnic.spec.distributedVirtualPort.switchUuid
|
|
except AttributeError:
|
|
changed_vds = True
|
|
changed_list.append("Virtual Port")
|
|
if changed_vds:
|
|
results['switch_previous'] = self.find_dvs_by_uuid(dvs_uuid)
|
|
results['portgroup_previous'] = self.vnic.spec.portgroup
|
|
portgroups = self.get_all_port_groups_by_host(host_system=self.esxi_host_obj)
|
|
for portgroup in portgroups:
|
|
if portgroup.spec.name == self.vnic.spec.portgroup:
|
|
results['switch_previous'] = portgroup.spec.vswitchName
|
|
|
|
results['services'] = self.create_enabled_services_string()
|
|
# Check configuration of service types (only if default TCP/IP stack is used)
|
|
if self.vnic.spec.netStackInstanceKey == 'defaultTcpipStack':
|
|
service_type_vmks = self.get_all_vmks_by_service_type()
|
|
if (self.enable_vmotion and self.vnic.device not in service_type_vmks['vmotion']) or \
|
|
(not self.enable_vmotion and self.vnic.device in service_type_vmks['vmotion']):
|
|
changed_services = changed_service_vmotion = True
|
|
|
|
if (self.enable_mgmt and self.vnic.device not in service_type_vmks['management']) or \
|
|
(not self.enable_mgmt and self.vnic.device in service_type_vmks['management']):
|
|
changed_services = changed_service_mgmt = True
|
|
|
|
if (self.enable_ft and self.vnic.device not in service_type_vmks['faultToleranceLogging']) or \
|
|
(not self.enable_ft and self.vnic.device in service_type_vmks['faultToleranceLogging']):
|
|
changed_services = changed_service_ft = True
|
|
|
|
if (self.enable_vsan and self.vnic.device not in service_type_vmks['vsan']) or \
|
|
(not self.enable_vsan and self.vnic.device in service_type_vmks['vsan']):
|
|
changed_services = changed_service_vsan = True
|
|
|
|
if (self.enable_provisioning and self.vnic.device not in service_type_vmks['vSphereProvisioning']) or \
|
|
(not self.enable_provisioning and self.vnic.device in service_type_vmks['vSphereProvisioning']):
|
|
changed_services = changed_service_prov = True
|
|
|
|
if (self.enable_replication and self.vnic.device not in service_type_vmks['vSphereReplication']) or \
|
|
(not self.enable_provisioning and self.vnic.device in service_type_vmks['vSphereReplication']):
|
|
changed_services = changed_service_rep = True
|
|
|
|
if (self.enable_replication_nfc and self.vnic.device not in service_type_vmks['vSphereReplicationNFC']) or \
|
|
(not self.enable_provisioning and self.vnic.device in service_type_vmks['vSphereReplicationNFC']):
|
|
changed_services = changed_service_rep_nfc = True
|
|
if changed_services:
|
|
changed_list.append("services")
|
|
|
|
if changed_settings or changed_vds or changed_services:
|
|
changed = True
|
|
if self.module.check_mode:
|
|
changed_suffix = ' would be updated'
|
|
else:
|
|
changed_suffix = ' updated'
|
|
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 = "VMkernel Adapter " + message + changed_suffix
|
|
if changed_settings or changed_vds:
|
|
vnic_config = vim.host.VirtualNic.Specification()
|
|
ip_spec = vim.host.IpConfig()
|
|
if self.network_type == 'dhcp':
|
|
ip_spec.dhcp = True
|
|
else:
|
|
ip_spec.dhcp = False
|
|
ip_spec.ipAddress = self.ip_address
|
|
ip_spec.subnetMask = self.subnet_mask
|
|
if self.default_gateway:
|
|
vnic_config.ipRouteSpec = vim.host.VirtualNic.IpRouteSpec()
|
|
vnic_config.ipRouteSpec.ipRouteConfig = vim.host.IpRouteConfig()
|
|
vnic_config.ipRouteSpec.ipRouteConfig.defaultGateway = self.default_gateway
|
|
else:
|
|
vnic_config.ipRouteSpec = vim.host.VirtualNic.IpRouteSpec()
|
|
vnic_config.ipRouteSpec.ipRouteConfig = vim.host.IpRouteConfig()
|
|
|
|
vnic_config.ip = ip_spec
|
|
vnic_config.mtu = self.mtu
|
|
|
|
if changed_vds:
|
|
if self.vswitch_name:
|
|
vnic_config.portgroup = self.port_group_name
|
|
elif self.vds_name:
|
|
vnic_config.distributedVirtualPort = vim.dvs.PortConnection()
|
|
vnic_config.distributedVirtualPort.switchUuid = self.dv_switch_obj.uuid
|
|
vnic_config.distributedVirtualPort.portgroupKey = self.port_group_obj.key
|
|
|
|
try:
|
|
if not self.module.check_mode:
|
|
self.esxi_host_obj.configManager.networkSystem.UpdateVirtualNic(self.vnic.device, vnic_config)
|
|
except vim.fault.NotFound as not_found:
|
|
self.module.fail_json(
|
|
msg="Failed to update vmk as virtual network adapter cannot be found %s" %
|
|
to_native(not_found.msg)
|
|
)
|
|
except vim.fault.HostConfigFault as host_config_fault:
|
|
self.module.fail_json(
|
|
msg="Failed to update vmk due to host config issues : %s" %
|
|
to_native(host_config_fault.msg)
|
|
)
|
|
except vim.fault.InvalidState as invalid_state:
|
|
self.module.fail_json(
|
|
msg="Failed to update vmk as ipv6 address is specified in an ipv4 only system : %s" %
|
|
to_native(invalid_state.msg)
|
|
)
|
|
except vmodl.fault.InvalidArgument as invalid_arg:
|
|
self.module.fail_json(
|
|
msg="Failed to update vmk as IP address or Subnet Mask in the IP configuration"
|
|
"are invalid or PortGroup does not exist : %s" % to_native(invalid_arg.msg)
|
|
)
|
|
|
|
if changed_services:
|
|
changed_list.append("Services")
|
|
services_previous = []
|
|
vnic_manager = self.esxi_host_obj.configManager.virtualNicManager
|
|
|
|
if changed_service_mgmt:
|
|
if self.vnic.device in service_type_vmks['management']:
|
|
services_previous.append('Mgmt')
|
|
operation = 'select' if self.enable_mgmt else 'deselect'
|
|
self.set_service_type(
|
|
vnic_manager=vnic_manager, vmk=self.vnic, service_type='management', operation=operation
|
|
)
|
|
|
|
if changed_service_vmotion:
|
|
if self.vnic.device in service_type_vmks['vmotion']:
|
|
services_previous.append('vMotion')
|
|
operation = 'select' if self.enable_vmotion else 'deselect'
|
|
self.set_service_type(
|
|
vnic_manager=vnic_manager, vmk=self.vnic, service_type='vmotion', operation=operation
|
|
)
|
|
|
|
if changed_service_ft:
|
|
if self.vnic.device in service_type_vmks['faultToleranceLogging']:
|
|
services_previous.append('FT')
|
|
operation = 'select' if self.enable_ft else 'deselect'
|
|
self.set_service_type(
|
|
vnic_manager=vnic_manager, vmk=self.vnic, service_type='faultToleranceLogging', operation=operation
|
|
)
|
|
|
|
if changed_service_prov:
|
|
if self.vnic.device in service_type_vmks['vSphereProvisioning']:
|
|
services_previous.append('Prov')
|
|
operation = 'select' if self.enable_provisioning else 'deselect'
|
|
self.set_service_type(
|
|
vnic_manager=vnic_manager, vmk=self.vnic, service_type='vSphereProvisioning', operation=operation
|
|
)
|
|
|
|
if changed_service_rep:
|
|
if self.vnic.device in service_type_vmks['vSphereReplication']:
|
|
services_previous.append('Repl')
|
|
operation = 'select' if self.enable_replication else 'deselect'
|
|
self.set_service_type(
|
|
vnic_manager=vnic_manager, vmk=self.vnic, service_type='vSphereReplication', operation=operation
|
|
)
|
|
|
|
if changed_service_rep_nfc:
|
|
if self.vnic.device in service_type_vmks['vSphereReplicationNFC']:
|
|
services_previous.append('Repl_NFC')
|
|
operation = 'select' if self.enable_replication_nfc else 'deselect'
|
|
self.set_service_type(
|
|
vnic_manager=vnic_manager, vmk=self.vnic, service_type='vSphereReplicationNFC', operation=operation
|
|
)
|
|
|
|
if changed_service_vsan:
|
|
if self.vnic.device in service_type_vmks['vsan']:
|
|
services_previous.append('VSAN')
|
|
if self.enable_vsan:
|
|
results['vsan'] = self.set_vsan_service_type()
|
|
else:
|
|
self.set_service_type(
|
|
vnic_manager=vnic_manager, vmk=self.vnic, service_type='vsan', operation=operation
|
|
)
|
|
|
|
results['services_previous'] = ', '.join(services_previous)
|
|
else:
|
|
message = "VMkernel Adapter already configured properly"
|
|
|
|
results['changed'] = changed
|
|
results['msg'] = message
|
|
results['device'] = self.vnic.device
|
|
self.module.exit_json(**results)
|
|
|
|
def find_dvs_by_uuid(self, uuid):
|
|
"""
|
|
Find DVS by UUID
|
|
Returns: DVS name
|
|
"""
|
|
dvs_list = get_all_objs(self.content, [vim.DistributedVirtualSwitch])
|
|
for dvs in dvs_list:
|
|
if dvs.uuid == uuid:
|
|
return dvs.summary.name
|
|
return None
|
|
|
|
def find_dvspg_by_key(self, dv_switch, portgroup_key):
|
|
"""
|
|
Find dvPortgroup by key
|
|
Returns: dvPortgroup name
|
|
"""
|
|
|
|
portgroups = dv_switch.portgroup
|
|
|
|
for portgroup in portgroups:
|
|
if portgroup.key == portgroup_key:
|
|
return portgroup.name
|
|
|
|
return None
|
|
|
|
def set_vsan_service_type(self):
|
|
"""
|
|
Set VSAN service type
|
|
Returns: result of UpdateVsan_Task
|
|
|
|
"""
|
|
result = None
|
|
vsan_system = self.esxi_host_obj.configManager.vsanSystem
|
|
|
|
vsan_port_config = vim.vsan.host.ConfigInfo.NetworkInfo.PortConfig()
|
|
vsan_port_config.device = self.vnic.device
|
|
|
|
vsan_config = vim.vsan.host.ConfigInfo()
|
|
vsan_config.networkInfo = vim.vsan.host.ConfigInfo.NetworkInfo()
|
|
vsan_config.networkInfo.port = [vsan_port_config]
|
|
if not self.module.check_mode:
|
|
try:
|
|
vsan_task = vsan_system.UpdateVsan_Task(vsan_config)
|
|
wait_for_task(vsan_task)
|
|
except TaskError as task_err:
|
|
self.module.fail_json(
|
|
msg="Failed to set service type to vsan for %s : %s" % (self.vnic.device, to_native(task_err))
|
|
)
|
|
return result
|
|
|
|
def host_vmk_create(self):
|
|
"""
|
|
Create VMKernel
|
|
Returns: NA
|
|
|
|
"""
|
|
results = dict(changed=False, message='')
|
|
if self.vswitch_name:
|
|
results['switch'] = self.vswitch_name
|
|
elif self.vds_name:
|
|
results['switch'] = self.vds_name
|
|
results['portgroup'] = self.port_group_name
|
|
|
|
vnic_config = vim.host.VirtualNic.Specification()
|
|
|
|
ip_spec = vim.host.IpConfig()
|
|
results['ipv4'] = self.network_type
|
|
if self.network_type == 'dhcp':
|
|
ip_spec.dhcp = True
|
|
else:
|
|
ip_spec.dhcp = False
|
|
results['ipv4_ip'] = self.ip_address
|
|
results['ipv4_sm'] = self.subnet_mask
|
|
ip_spec.ipAddress = self.ip_address
|
|
ip_spec.subnetMask = self.subnet_mask
|
|
if self.default_gateway:
|
|
vnic_config.ipRouteSpec = vim.host.VirtualNic.IpRouteSpec()
|
|
vnic_config.ipRouteSpec.ipRouteConfig = vim.host.IpRouteConfig()
|
|
vnic_config.ipRouteSpec.ipRouteConfig.defaultGateway = self.default_gateway
|
|
vnic_config.ip = ip_spec
|
|
|
|
results['mtu'] = self.mtu
|
|
vnic_config.mtu = self.mtu
|
|
|
|
results['tcpip_stack'] = self.tcpip_stack
|
|
vnic_config.netStackInstanceKey = self.get_api_net_stack_instance(self.tcpip_stack)
|
|
|
|
vmk_device = None
|
|
try:
|
|
if self.module.check_mode:
|
|
results['msg'] = "VMkernel Adapter would be created"
|
|
else:
|
|
if self.vswitch_name:
|
|
vmk_device = self.esxi_host_obj.configManager.networkSystem.AddVirtualNic(
|
|
self.port_group_name,
|
|
vnic_config
|
|
)
|
|
elif self.vds_name:
|
|
vnic_config.distributedVirtualPort = vim.dvs.PortConnection()
|
|
vnic_config.distributedVirtualPort.switchUuid = self.dv_switch_obj.uuid
|
|
vnic_config.distributedVirtualPort.portgroupKey = self.port_group_obj.key
|
|
vmk_device = self.esxi_host_obj.configManager.networkSystem.AddVirtualNic(portgroup="", nic=vnic_config)
|
|
results['msg'] = "VMkernel Adapter created"
|
|
results['changed'] = True
|
|
results['device'] = vmk_device
|
|
if self.network_type != 'dhcp':
|
|
if self.default_gateway:
|
|
results['ipv4_gw'] = self.default_gateway
|
|
else:
|
|
results['ipv4_gw'] = "No override"
|
|
results['services'] = self.create_enabled_services_string()
|
|
except vim.fault.AlreadyExists as already_exists:
|
|
self.module.fail_json(
|
|
msg="Failed to add vmk as portgroup already has a virtual network adapter %s" %
|
|
to_native(already_exists.msg)
|
|
)
|
|
except vim.fault.HostConfigFault as host_config_fault:
|
|
self.module.fail_json(
|
|
msg="Failed to add vmk due to host config issues : %s" %
|
|
to_native(host_config_fault.msg)
|
|
)
|
|
except vim.fault.InvalidState as invalid_state:
|
|
self.module.fail_json(
|
|
msg="Failed to add vmk as ipv6 address is specified in an ipv4 only system : %s" %
|
|
to_native(invalid_state.msg)
|
|
)
|
|
except vmodl.fault.InvalidArgument as invalid_arg:
|
|
self.module.fail_json(
|
|
msg="Failed to add vmk as IP address or Subnet Mask in the IP configuration "
|
|
"are invalid or PortGroup does not exist : %s" % to_native(invalid_arg.msg)
|
|
)
|
|
|
|
# do service type configuration
|
|
if self.tcpip_stack == 'default' and not all(
|
|
option is False for option in [self.enable_vsan, self.enable_vmotion,
|
|
self.enable_mgmt, self.enable_ft,
|
|
self.enable_provisioning, self.enable_replication,
|
|
self.enable_replication_nfc]):
|
|
self.vnic = self.get_vmkernel_by_device(device_name=vmk_device)
|
|
|
|
# VSAN
|
|
if self.enable_vsan:
|
|
results['vsan'] = self.set_vsan_service_type()
|
|
|
|
# Other service type
|
|
host_vnic_manager = self.esxi_host_obj.configManager.virtualNicManager
|
|
if self.enable_vmotion:
|
|
self.set_service_type(host_vnic_manager, self.vnic, 'vmotion')
|
|
|
|
if self.enable_mgmt:
|
|
self.set_service_type(host_vnic_manager, self.vnic, 'management')
|
|
|
|
if self.enable_ft:
|
|
self.set_service_type(host_vnic_manager, self.vnic, 'faultToleranceLogging')
|
|
|
|
if self.enable_provisioning:
|
|
self.set_service_type(host_vnic_manager, self.vnic, 'vSphereProvisioning')
|
|
|
|
if self.enable_replication:
|
|
self.set_service_type(host_vnic_manager, self.vnic, 'vSphereReplication')
|
|
|
|
if self.enable_replication_nfc:
|
|
self.set_service_type(host_vnic_manager, self.vnic, 'vSphereReplicationNFC')
|
|
|
|
self.module.exit_json(**results)
|
|
|
|
def set_service_type(self, vnic_manager, vmk, service_type, operation='select'):
|
|
"""
|
|
Set service type to given VMKernel
|
|
Args:
|
|
vnic_manager: Virtual NIC manager object
|
|
vmk: VMkernel managed object
|
|
service_type: Name of service type
|
|
operation: Select to select service type, deselect to deselect service type
|
|
|
|
"""
|
|
try:
|
|
if operation == 'select':
|
|
if not self.module.check_mode:
|
|
vnic_manager.SelectVnicForNicType(service_type, vmk.device)
|
|
elif operation == 'deselect':
|
|
if not self.module.check_mode:
|
|
vnic_manager.DeselectVnicForNicType(service_type, vmk.device)
|
|
except vmodl.fault.InvalidArgument as invalid_arg:
|
|
self.module.fail_json(
|
|
msg="Failed to %s VMK service type '%s' on '%s' due to : %s" %
|
|
(operation, service_type, vmk.device, to_native(invalid_arg.msg))
|
|
)
|
|
|
|
def get_all_vmks_by_service_type(self):
|
|
"""
|
|
Return information about service types and VMKernel
|
|
Returns: Dictionary of service type as key and VMKernel list as value
|
|
|
|
"""
|
|
service_type_vmk = dict(
|
|
vmotion=[],
|
|
vsan=[],
|
|
management=[],
|
|
faultToleranceLogging=[],
|
|
vSphereProvisioning=[],
|
|
vSphereReplication=[],
|
|
vSphereReplicationNFC=[],
|
|
)
|
|
|
|
for service_type in list(service_type_vmk):
|
|
vmks_list = self.query_service_type_for_vmks(service_type)
|
|
service_type_vmk[service_type] = vmks_list
|
|
return service_type_vmk
|
|
|
|
def query_service_type_for_vmks(self, service_type):
|
|
"""
|
|
Return list of VMKernels
|
|
Args:
|
|
service_type: Name of service type
|
|
|
|
Returns: List of VMKernel which belongs to that service type
|
|
|
|
"""
|
|
vmks_list = []
|
|
query = None
|
|
try:
|
|
query = self.esxi_host_obj.configManager.virtualNicManager.QueryNetConfig(service_type)
|
|
except vim.fault.HostConfigFault as config_fault:
|
|
self.module.fail_json(
|
|
msg="Failed to get all VMKs for service type %s due to host config fault : %s" %
|
|
(service_type, to_native(config_fault.msg))
|
|
)
|
|
except vmodl.fault.InvalidArgument as invalid_argument:
|
|
self.module.fail_json(
|
|
msg="Failed to get all VMKs for service type %s due to invalid arguments : %s" %
|
|
(service_type, to_native(invalid_argument.msg))
|
|
)
|
|
|
|
if not query.selectedVnic:
|
|
return vmks_list
|
|
selected_vnics = [vnic for vnic in query.selectedVnic]
|
|
vnics_with_service_type = [vnic.device for vnic in query.candidateVnic if vnic.key in selected_vnics]
|
|
return vnics_with_service_type
|
|
|
|
def create_enabled_services_string(self):
|
|
"""Create services list"""
|
|
services = []
|
|
if self.enable_mgmt:
|
|
services.append('Mgmt')
|
|
if self.enable_vmotion:
|
|
services.append('vMotion')
|
|
if self.enable_ft:
|
|
services.append('FT')
|
|
if self.enable_vsan:
|
|
services.append('VSAN')
|
|
if self.enable_provisioning:
|
|
services.append('Prov')
|
|
if self.enable_replication:
|
|
services.append('Repl')
|
|
if self.enable_replication_nfc:
|
|
services.append('Repl_NFC')
|
|
return ', '.join(services)
|
|
|
|
@staticmethod
|
|
def get_api_net_stack_instance(tcpip_stack):
|
|
"""Get TCP/IP stack instance name or key"""
|
|
net_stack_instance = None
|
|
if tcpip_stack == 'default':
|
|
net_stack_instance = 'defaultTcpipStack'
|
|
elif tcpip_stack == 'provisioning':
|
|
net_stack_instance = 'vSphereProvisioning'
|
|
# vmotion and vxlan stay the same
|
|
elif tcpip_stack == 'vmotion':
|
|
net_stack_instance = 'vmotion'
|
|
elif tcpip_stack == 'vxlan':
|
|
net_stack_instance = 'vxlan'
|
|
elif tcpip_stack == 'defaultTcpipStack':
|
|
net_stack_instance = 'default'
|
|
elif tcpip_stack == 'vSphereProvisioning':
|
|
net_stack_instance = 'provisioning'
|
|
# vmotion and vxlan stay the same
|
|
elif tcpip_stack == 'vmotion':
|
|
net_stack_instance = 'vmotion'
|
|
elif tcpip_stack == 'vxlan':
|
|
net_stack_instance = 'vxlan'
|
|
return net_stack_instance
|
|
|
|
|
|
def main():
|
|
"""Main"""
|
|
argument_spec = vmware_argument_spec()
|
|
argument_spec.update(dict(
|
|
esxi_hostname=dict(required=True, type='str'),
|
|
portgroup_name=dict(required=True, type='str', aliases=['portgroup']),
|
|
ip_address=dict(removed_in_version=2.9, type='str'),
|
|
subnet_mask=dict(removed_in_version=2.9, type='str'),
|
|
mtu=dict(required=False, type='int', default=1500),
|
|
device=dict(type='str'),
|
|
enable_vsan=dict(required=False, type='bool', default=False),
|
|
enable_vmotion=dict(required=False, type='bool', default=False),
|
|
enable_mgmt=dict(required=False, type='bool', default=False),
|
|
enable_ft=dict(required=False, type='bool', default=False),
|
|
enable_provisioning=dict(type='bool', default=False),
|
|
enable_replication=dict(type='bool', default=False),
|
|
enable_replication_nfc=dict(type='bool', default=False),
|
|
vswitch_name=dict(required=False, type='str', aliases=['vswitch']),
|
|
dvswitch_name=dict(required=False, type='str', aliases=['dvswitch']),
|
|
network=dict(
|
|
type='dict',
|
|
options=dict(
|
|
type=dict(type='str', default='static', choices=['static', 'dhcp']),
|
|
ip_address=dict(type='str'),
|
|
subnet_mask=dict(type='str'),
|
|
default_gateway=dict(type='str'),
|
|
tcpip_stack=dict(type='str', default='default', choices=['default', 'provisioning', 'vmotion', 'vxlan']),
|
|
),
|
|
default=dict(
|
|
type='static',
|
|
tcpip_stack='default',
|
|
),
|
|
),
|
|
state=dict(
|
|
type='str',
|
|
default='present',
|
|
choices=['absent', 'present']
|
|
),
|
|
))
|
|
|
|
module = AnsibleModule(argument_spec=argument_spec,
|
|
mutually_exclusive=[
|
|
['vswitch_name', 'dvswitch_name'],
|
|
['tcpip_stack', 'enable_vsan'],
|
|
['tcpip_stack', 'enable_vmotion'],
|
|
['tcpip_stack', 'enable_mgmt'],
|
|
['tcpip_stack', 'enable_ft'],
|
|
['tcpip_stack', 'enable_provisioning'],
|
|
['tcpip_stack', 'enable_replication'],
|
|
['tcpip_stack', 'enable_replication_nfc'],
|
|
],
|
|
required_one_of=[
|
|
['vswitch_name', 'dvswitch_name'],
|
|
['portgroup_name', 'device'],
|
|
],
|
|
required_if=[
|
|
['state', 'present', ['portgroup_name']],
|
|
['state', 'absent', ['device']]
|
|
],
|
|
supports_check_mode=True)
|
|
|
|
pyv = PyVmomiHelper(module)
|
|
pyv.ensure()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|