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_ipv6
21short_description: Enables/Disables IPv6 support for an ESXi host system
22description:
23- This module can be used to enable or disable IPv6 support for ESXi host systems in given vCenter infrastructure.
24- It also checks if the host needs to be restarted.
25version_added: 2.8
26author:
27- Christian Kotte (@ckotte) <christian.kotte@gmx.de>
28notes:
29- Tested on vSphere 6.5
30requirements:
31- python >= 2.6
32- PyVmomi
33options:
34  state:
35     description:
36        - Enable or disable IPv6 support.
37        - You need to reboot the ESXi host if you change the configuration.
38     type: str
39     choices: [ enabled, disabled ]
40     default: 'enabled'
41  esxi_hostname:
42    description:
43    - Name of the host system to work with.
44    - This is required parameter if C(cluster_name) is not specified.
45    type: str
46  cluster_name:
47    description:
48    - Name of the cluster from which all host systems will be used.
49    - This is required parameter if C(esxi_hostname) is not specified.
50    type: str
51extends_documentation_fragment: vmware.documentation
52'''
53
54EXAMPLES = r'''
55- name: Enable IPv6 for an host system
56  vmware_host_ipv6:
57    hostname: '{{ vcenter_hostname }}'
58    username: '{{ vcenter_username }}'
59    password: '{{ vcenter_password }}'
60    esxi_hostname: '{{ esxi_hostname }}'
61    state: enabled
62    validate_certs: no
63  delegate_to: localhost
64
65- name: Disable IPv6 for an host system
66  vmware_host_ipv6:
67    hostname: '{{ vcenter_hostname }}'
68    username: '{{ vcenter_username }}'
69    password: '{{ vcenter_password }}'
70    esxi_hostname: '{{ esxi_hostname }}'
71    state: disabled
72    validate_certs: no
73  delegate_to: localhost
74
75- name: Disable IPv6 for all host systems from cluster
76  vmware_host_ipv6:
77    hostname: '{{ vcenter_hostname }}'
78    username: '{{ vcenter_username }}'
79    password: '{{ vcenter_password }}'
80    cluster_name: '{{ cluster_name }}'
81    state: disabled
82    validate_certs: no
83  delegate_to: localhost
84'''
85
86RETURN = r'''
87result:
88    description: metadata about host system's IPv6 configuration
89    returned: always
90    type: dict
91    sample: {
92        "esxi01": {
93            "changed": false,
94            "msg": "IPv6 is already enabled and active for host 'esxi01'",
95        },
96    }
97'''
98
99try:
100    from pyVmomi import vim, vmodl
101except ImportError:
102    pass
103
104from ansible.module_utils.basic import AnsibleModule
105from ansible.module_utils.vmware import PyVmomi, vmware_argument_spec
106from ansible.module_utils._text import to_native
107
108
109class VmwareHostIPv6(PyVmomi):
110    """Class to manage IPv6 for an ESXi host system"""
111    def __init__(self, module):
112        super(VmwareHostIPv6, self).__init__(module)
113        cluster_name = self.params.get('cluster_name')
114        esxi_host_name = self.params.get('esxi_hostname')
115        self.hosts = self.get_all_host_objs(cluster_name=cluster_name, esxi_host_name=esxi_host_name)
116        if not self.hosts:
117            self.module.fail_json(msg="Failed to find host system with given configuration.")
118
119    def ensure(self):
120        """Manage IPv6 for an ESXi host system"""
121        results = dict(changed=False, result=dict())
122        desired_state = self.module.params['state']
123
124        host_change_list = []
125        for host in self.hosts:
126            changed = False
127            results['result'][host.name] = dict(msg='')
128
129            host_network_system = host.configManager.networkSystem
130            host_network_info = host_network_system.networkInfo
131
132            if desired_state == 'enabled':
133                # Don't do anything if IPv6 is already enabled
134                if host_network_info.atBootIpV6Enabled:
135                    if host_network_info.ipV6Enabled:
136                        results['result'][host.name]['msg'] = "IPv6 is already enabled and active for host '%s'" % \
137                                                              host.name
138                    if not host_network_info.ipV6Enabled:
139                        results['result'][host.name]['msg'] = ("IPv6 is already enabled for host '%s', but a reboot"
140                                                               " is required!" % host.name)
141                # Enable IPv6
142                else:
143                    if not self.module.check_mode:
144                        try:
145                            config = vim.host.NetworkConfig()
146                            config.ipV6Enabled = True
147                            host_network_system.UpdateNetworkConfig(config, "modify")
148                            changed = True
149                            results['result'][host.name]['changed'] = True
150                            results['result'][host.name]['msg'] = "IPv6 enabled for host '%s'" % host.name
151                        except (vim.fault.AlreadyExists, vim.fault.NotFound):
152                            self.module.fail_json(msg="Network entity specified in the configuration for host '%s'"
153                                                  " already exists" % host.name)
154                        except vmodl.fault.InvalidArgument as invalid_argument:
155                            self.module.fail_json(msg="Invalid parameter specified for host '%s' : %s" %
156                                                  (host.name, to_native(invalid_argument.msg)))
157                        except vim.fault.HostConfigFault as config_fault:
158                            self.module.fail_json(msg="Failed to enable IPv6 for host '%s' due to : %s" %
159                                                  (host.name, to_native(config_fault.msg)))
160                        except vmodl.fault.NotSupported as not_supported:
161                            self.module.fail_json(msg="Failed to enable IPv6 for host '%s' due to : %s" %
162                                                  (host.name, to_native(not_supported.msg)))
163                        except (vmodl.RuntimeFault, vmodl.MethodFault) as runtime_fault:
164                            self.module.fail_json(msg="Failed to enable IPv6 for host '%s' due to : %s" %
165                                                  (host.name, to_native(runtime_fault.msg)))
166                    else:
167                        changed = True
168                        results['result'][host.name]['changed'] = True
169                        results['result'][host.name]['msg'] = "IPv6 will be enabled for host '%s'" % host.name
170            elif desired_state == 'disabled':
171                # Don't do anything if IPv6 is already disabled
172                if not host_network_info.atBootIpV6Enabled:
173                    if not host_network_info.ipV6Enabled:
174                        results['result'][host.name]['msg'] = "IPv6 is already disabled for host '%s'" % host.name
175                    if host_network_info.ipV6Enabled:
176                        changed = True
177                        results['result'][host.name]['msg'] = ("IPv6 is already disabled for host '%s',"
178                                                               " but a reboot is required!" % host.name)
179                # Disable IPv6
180                else:
181                    if not self.module.check_mode:
182                        try:
183                            config = vim.host.NetworkConfig()
184                            config.ipV6Enabled = False
185                            host_network_system.UpdateNetworkConfig(config, "modify")
186                            changed = True
187                            results['result'][host.name]['changed'] = True
188                            results['result'][host.name]['msg'] = "IPv6 disabled for host '%s'" % host.name
189                        except (vim.fault.AlreadyExists, vim.fault.NotFound):
190                            self.module.fail_json(msg="Network entity specified in the configuration for host '%s'"
191                                                  " already exists" % host.name)
192                        except vmodl.fault.InvalidArgument as invalid_argument:
193                            self.module.fail_json(msg="Invalid parameter specified for host '%s' : %s" %
194                                                  (host.name, to_native(invalid_argument.msg)))
195                        except vim.fault.HostConfigFault as config_fault:
196                            self.module.fail_json(msg="Failed to disable IPv6 for host '%s' due to : %s" %
197                                                  (host.name, to_native(config_fault.msg)))
198                        except vmodl.fault.NotSupported as not_supported:
199                            self.module.fail_json(msg="Failed to disable IPv6 for host '%s' due to : %s" %
200                                                  (host.name, to_native(not_supported.msg)))
201                        except (vmodl.RuntimeFault, vmodl.MethodFault) as runtime_fault:
202                            self.module.fail_json(msg="Failed to disable IPv6 for host '%s' due to : %s" %
203                                                  (host.name, to_native(runtime_fault.msg)))
204                    else:
205                        changed = True
206                        results['result'][host.name]['changed'] = True
207                        results['result'][host.name]['msg'] = "IPv6 will be disabled for host '%s'" % host.name
208
209            host_change_list.append(changed)
210
211        if any(host_change_list):
212            results['changed'] = True
213        self.module.exit_json(**results)
214
215
216def main():
217    """
218    Main
219    """
220    argument_spec = vmware_argument_spec()
221    argument_spec.update(
222        state=dict(default='enabled', choices=['enabled', 'disabled']),
223        esxi_hostname=dict(type='str', required=False),
224        cluster_name=dict(type='str', required=False),
225    )
226
227    module = AnsibleModule(argument_spec=argument_spec,
228                           required_one_of=[
229                               ['cluster_name', 'esxi_hostname'],
230                           ],
231                           supports_check_mode=True
232                           )
233
234    ipv6 = VmwareHostIPv6(module)
235    ipv6.ensure()
236
237
238if __name__ == '__main__':
239    main()
240