1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3 4# Copyright: (c) 2018, Christian Kotte <christian.kotte@gmx.de> 5# 6# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 7 8from __future__ import absolute_import, division, print_function 9__metaclass__ = type 10 11 12ANSIBLE_METADATA = { 13 'metadata_version': '1.1', 14 'status': ['preview'], 15 'supported_by': 'community' 16} 17 18DOCUMENTATION = r''' 19--- 20module: vmware_host_hyperthreading 21short_description: Enables/Disables Hyperthreading optimization for an ESXi host system 22description: 23- This module can be used to enable or disable Hyperthreading optimization for ESXi host systems in given vCenter infrastructure. 24- It also checks if Hyperthreading is activated/deactivated and if the host needs to be restarted. 25- The module informs the user if Hyperthreading is enabled but inactive because the processor is vulnerable to L1 Terminal Fault (L1TF). 26version_added: 2.8 27author: 28- Christian Kotte (@ckotte) 29notes: 30- Tested on vSphere 6.5 31requirements: 32- python >= 2.6 33- PyVmomi 34options: 35 state: 36 description: 37 - Enable or disable Hyperthreading. 38 - You need to reboot the ESXi host if you change the configuration. 39 - Make sure that Hyperthreading is enabled in the BIOS. Otherwise, it will be enabled, but never activated. 40 type: str 41 choices: [ enabled, disabled ] 42 default: 'enabled' 43 esxi_hostname: 44 description: 45 - Name of the host system to work with. 46 - This parameter is required if C(cluster_name) is not specified. 47 type: str 48 cluster_name: 49 description: 50 - Name of the cluster from which all host systems will be used. 51 - This parameter is required if C(esxi_hostname) is not specified. 52 type: str 53extends_documentation_fragment: vmware.documentation 54''' 55 56EXAMPLES = r''' 57- name: Enable Hyperthreading for an host system 58 vmware_host_hyperthreading: 59 hostname: '{{ vcenter_hostname }}' 60 username: '{{ vcenter_username }}' 61 password: '{{ vcenter_password }}' 62 esxi_hostname: '{{ esxi_hostname }}' 63 state: enabled 64 validate_certs: no 65 delegate_to: localhost 66 67- name: Disable Hyperthreading for an host system 68 vmware_host_hyperthreading: 69 hostname: '{{ vcenter_hostname }}' 70 username: '{{ vcenter_username }}' 71 password: '{{ vcenter_password }}' 72 esxi_hostname: '{{ esxi_hostname }}' 73 state: disabled 74 validate_certs: no 75 delegate_to: localhost 76 77- name: Disable Hyperthreading for all host systems from cluster 78 vmware_host_hyperthreading: 79 hostname: '{{ vcenter_hostname }}' 80 username: '{{ vcenter_username }}' 81 password: '{{ vcenter_password }}' 82 cluster_name: '{{ cluster_name }}' 83 state: disabled 84 validate_certs: no 85 delegate_to: localhost 86''' 87 88RETURN = r''' 89results: 90 description: metadata about host system's Hyperthreading configuration 91 returned: always 92 type: dict 93 sample: { 94 "esxi01": { 95 "msg": "Hyperthreading is already enabled and active for host 'esxi01'", 96 "state_current": "active", 97 "state": "enabled", 98 }, 99 } 100''' 101 102try: 103 from pyVmomi import vim, vmodl 104except ImportError: 105 pass 106 107from ansible.module_utils.basic import AnsibleModule 108from ansible.module_utils.vmware import PyVmomi, vmware_argument_spec 109from ansible.module_utils._text import to_native 110 111 112class VmwareHostHyperthreading(PyVmomi): 113 """Manage Hyperthreading for an ESXi host system""" 114 def __init__(self, module): 115 super(VmwareHostHyperthreading, self).__init__(module) 116 cluster_name = self.params.get('cluster_name') 117 esxi_host_name = self.params.get('esxi_hostname') 118 self.hosts = self.get_all_host_objs(cluster_name=cluster_name, esxi_host_name=esxi_host_name) 119 if not self.hosts: 120 self.module.fail_json(msg="Failed to find host system.") 121 122 def ensure(self): 123 """Manage Hyperthreading for an ESXi host system""" 124 results = dict(changed=False, result=dict()) 125 desired_state = self.params.get('state') 126 host_change_list = [] 127 for host in self.hosts: 128 changed = False 129 results['result'][host.name] = dict(msg='') 130 131 hyperthreading_info = host.config.hyperThread 132 133 results['result'][host.name]['state'] = desired_state 134 if desired_state == 'enabled': 135 # Don't do anything if Hyperthreading is already enabled 136 if hyperthreading_info.config: 137 if hyperthreading_info.active: 138 results['result'][host.name]['changed'] = False 139 results['result'][host.name]['state_current'] = "active" 140 results['result'][host.name]['msg'] = "Hyperthreading is enabled and active" 141 if not hyperthreading_info.active: 142 # L1 Terminal Fault (L1TF)/Foreshadow mitigation workaround (https://kb.vmware.com/s/article/55806) 143 option_manager = host.configManager.advancedOption 144 try: 145 mitigation = option_manager.QueryOptions('VMkernel.Boot.hyperthreadingMitigation') 146 except vim.fault.InvalidName: 147 mitigation = None 148 if mitigation and mitigation[0].value: 149 results['result'][host.name]['changed'] = False 150 results['result'][host.name]['state_current'] = "enabled" 151 results['result'][host.name]['msg'] = ("Hyperthreading is enabled, but not active because the" 152 " processor is vulnerable to L1 Terminal Fault (L1TF).") 153 else: 154 changed = results['result'][host.name]['changed'] = True 155 results['result'][host.name]['state_current'] = "enabled" 156 results['result'][host.name]['msg'] = ("Hyperthreading is enabled, but not active." 157 " A reboot is required!") 158 # Enable Hyperthreading 159 else: 160 # Check if Hyperthreading is available 161 if hyperthreading_info.available: 162 if not self.module.check_mode: 163 try: 164 host.configManager.cpuScheduler.EnableHyperThreading() 165 changed = results['result'][host.name]['changed'] = True 166 results['result'][host.name]['state_previous'] = "disabled" 167 results['result'][host.name]['state_current'] = "enabled" 168 results['result'][host.name]['msg'] = ( 169 "Hyperthreading enabled for host. Reboot the host to activate it." 170 ) 171 except vmodl.fault.NotSupported as not_supported: 172 # This should never happen since Hyperthreading is available 173 self.module.fail_json( 174 msg="Failed to enable Hyperthreading for host '%s' : %s" % 175 (host.name, to_native(not_supported.msg)) 176 ) 177 except (vmodl.RuntimeFault, vmodl.MethodFault) as runtime_fault: 178 self.module.fail_json( 179 msg="Failed to enable Hyperthreading for host '%s' due to : %s" % 180 (host.name, to_native(runtime_fault.msg)) 181 ) 182 else: 183 changed = results['result'][host.name]['changed'] = True 184 results['result'][host.name]['state_previous'] = "disabled" 185 results['result'][host.name]['state_current'] = "enabled" 186 results['result'][host.name]['msg'] = "Hyperthreading will be enabled" 187 else: 188 self.module.fail_json(msg="Hyperthreading optimization is not available for host '%s'" % host.name) 189 elif desired_state == 'disabled': 190 # Don't do anything if Hyperthreading is already disabled 191 if not hyperthreading_info.config: 192 if not hyperthreading_info.active: 193 results['result'][host.name]['changed'] = False 194 results['result'][host.name]['state_current'] = "inactive" 195 results['result'][host.name]['msg'] = "Hyperthreading is disabled and inactive" 196 if hyperthreading_info.active: 197 changed = results['result'][host.name]['changed'] = True 198 results['result'][host.name]['state_current'] = "disabled" 199 results['result'][host.name]['msg'] = ("Hyperthreading is already disabled" 200 " but still active. A reboot is required!") 201 # Disable Hyperthreading 202 else: 203 # Check if Hyperthreading is available 204 if hyperthreading_info.available: 205 if not self.module.check_mode: 206 try: 207 host.configManager.cpuScheduler.DisableHyperThreading() 208 changed = results['result'][host.name]['changed'] = True 209 results['result'][host.name]['state_previous'] = "enabled" 210 results['result'][host.name]['state_current'] = "disabled" 211 results['result'][host.name]['msg'] = ( 212 "Hyperthreading disabled. Reboot the host to deactivate it." 213 ) 214 except vmodl.fault.NotSupported as not_supported: 215 # This should never happen since Hyperthreading is available 216 self.module.fail_json( 217 msg="Failed to disable Hyperthreading for host '%s' : %s" % 218 (host.name, to_native(not_supported.msg)) 219 ) 220 except (vmodl.RuntimeFault, vmodl.MethodFault) as runtime_fault: 221 self.module.fail_json( 222 msg="Failed to disable Hyperthreading for host '%s' due to : %s" % 223 (host.name, to_native(runtime_fault.msg)) 224 ) 225 else: 226 changed = results['result'][host.name]['changed'] = True 227 results['result'][host.name]['state_previous'] = "enabled" 228 results['result'][host.name]['state_current'] = "disabled" 229 results['result'][host.name]['msg'] = "Hyperthreading will be disabled" 230 else: 231 self.module.fail_json(msg="Hyperthreading optimization is not available for host '%s'" % host.name) 232 233 host_change_list.append(changed) 234 235 if any(host_change_list): 236 results['changed'] = True 237 self.module.exit_json(**results) 238 239 240def main(): 241 """Main""" 242 argument_spec = vmware_argument_spec() 243 argument_spec.update( 244 state=dict(default='enabled', choices=['enabled', 'disabled']), 245 esxi_hostname=dict(type='str', required=False), 246 cluster_name=dict(type='str', required=False), 247 ) 248 249 module = AnsibleModule(argument_spec=argument_spec, 250 required_one_of=[ 251 ['cluster_name', 'esxi_hostname'], 252 ], 253 supports_check_mode=True 254 ) 255 256 hyperthreading = VmwareHostHyperthreading(module) 257 hyperthreading.ensure() 258 259 260if __name__ == '__main__': 261 main() 262