Repository: ansible/ansible
Pull request: VMware: add new module vmware_guest_video
##### SUMMARY
<!--- Describe the change below, including rationale and design decisions -->
Add a new module named "vmware_guest_video" to manage virtual machines's video card settings.
<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->
Fixes #49372
##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
- New Module Pull Request

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below -->
vmware_guest_video
##### ADDITIONAL INFORMATION
<!--- Include additional information to help people understand the change here -->
<!--- A step-by-step reproduction of the problem is helpful if there is no related issue -->
- hosts: localhost
  tasks:
    - name: test video card
      vmware_guest_video:
        validate_certs: no
        hostname: test.com
        username: root
        password: admin
        name: dw_test_vm
        datacenter: test-dc
        video_card:
          use_auto_detect: false
          display_number: 2
          video_memory_mb: 8
          enable_3D: true
          renderer_3D: automatic
          memory_3D_mb: 512
      register: video
<!--- Paste verbatim command output below, e.g. before and after your change -->
```paste below

```


Problem statement:
VMware new module request vmware_guest_video
<!--- Verify first that your feature was not already discussed on GitHub -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Describe the new feature/improvement briefly below -->
We want to modify the video related configuration of the VM:
video_card_settings
display_number
video_memory
enable_3d
3d_renderer
3d_memory
##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
<!--- Write the short name of the new feature, module, plugin or task below -->
vmware_guest_video
##### ADDITIONAL INFORMATION
<!--- Describe how the feature would be used, why it is needed and what it would solve -->
It can be used to re-configure VM to set the video related configurations.
<!--- Paste example playbooks or commands between quotes below -->
```yaml
- name: Add disks to virtual machine using UUID
  vmware_guest_disk:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    datacenter: "{{ datacenter_name }}"
    validate_certs: no
    name: test_vm
    video_card:
        video_card_settings: custom
        display_number: 1
        video_memory_mb: 8
        enable_3d: true
        3d_renderer: automatic
        3d_memory_mb: 256
  delegate_to: localhost
  register: video_card

```

<!--- HINT: You can also paste gist.github.com links for larger files -->

Hints from issue discussion:
Hi @Tomorrow9, thank you for submitting this issue!

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: thank_submitter --->
Files identified in the description:
None

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
@Tomorrow9 Thanks for requesting this feature. Since `vmware_guest` is already overloaded with features, I think we should have a separate module for `video` settings. 

Let me know your views on this. 
Yeah, I agree with you, I think vmware_guest can only contain vm deployment required virtual hardware configuration, as to vm re-configuration or modification, we can use other separate modules to do this to avoid vmware_guest being more complicated and hard to maintain. 
But there is one thing concerned is duplicate code in both modules. e.g., there is vmware_guest_disk module, and at the same time we have requirement that create VM with multiple controllers and disks #47028. So in the future if we have a good way to put these hardwares related operation in one class or file? 
Just one concern. Thanks.

Commit message: modify doc issue

Patch to review:
diff --git a/lib/ansible/modules/cloud/vmware/vmware_guest_video.py b/lib/ansible/modules/cloud/vmware/vmware_guest_video.py
new file mode 100644
index 00000000000000..714527b050b1a6
--- /dev/null
+++ b/lib/ansible/modules/cloud/vmware/vmware_guest_video.py
@@ -0,0 +1,325 @@
+#!/usr/bin/python
+#  -*- coding: utf-8 -*-
+#  Copyright: (c) 2018, Ansible Project
+#  Copyright: (c) 2018, Diane Wang <dianew@vmware.com>
+#  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_guest_video
+short_description: Modify video card configurations of specified virtual machine in given vCenter infrastructure
+description:
+    - This module is used to reconfigure video card settings of given virtual machine.
+    - All parameters and VMware object names are case sensitive.
+version_added: '2.8'
+author:
+    - Diane Wang (@Tomorrow9) <dianew@vmware.com>
+notes:
+    - Tested on vSphere 6.0, 6.5 and 6.7
+requirements:
+    - "python >= 2.6"
+    - PyVmomi
+options:
+   name:
+     description:
+     - Name of the virtual machine.
+     - This is a required parameter, if parameter C(uuid) is not supplied.
+   uuid:
+     description:
+     - UUID of the instance to gather facts if known, this is VMware's unique identifier.
+     - This is a required parameter, if parameter C(name) is not supplied.
+   folder:
+     description:
+     - Destination folder, absolute or relative path to find an existing guest.
+     - This is a required parameter, only if multiple VMs are found with same name.
+     - The folder should include the datacenter. ESXi server's datacenter is ha-datacenter.
+     - 'Examples:'
+     - '   folder: /ha-datacenter/vm'
+     - '   folder: ha-datacenter/vm'
+     - '   folder: /datacenter1/vm'
+     - '   folder: datacenter1/vm'
+     - '   folder: /datacenter1/vm/folder1'
+     - '   folder: datacenter1/vm/folder1'
+     - '   folder: /folder1/datacenter1/vm'
+     - '   folder: folder1/datacenter1/vm'
+     - '   folder: /folder1/datacenter1/vm/folder2'
+   datacenter:
+     default: ha-datacenter
+     description:
+     - The datacenter name to which virtual machine belongs to.
+     - This parameter is case sensitive.
+   gather_video_facts:
+     description:
+     - If set to True, return settings of the video card, other attributes are ignored.
+     - If set to False, will do reconfiguration and return video card settings.
+     type: bool
+     default: 'no'
+   use_auto_detect:
+     description:
+     - 'If set to True, applies common video settings to the guest operating system, attributes C(display_number) and C(video_memory_mb) are ignored.'
+     - 'If set to False, the number of display and the total video memory will be reconfigured using C(display_number) and C(video_memory_mb).'
+     type: bool
+   display_number:
+     description:
+     - The number of display. Valid value from 1 to 10. The maximum display number is 4 on vCenter 6.0, 6.5 web UI.
+   video_memory_mb:
+     description:
+     - Valid total video memory range of virtual machine is from 1.172 MB to 256 MB on ESXi 6.7, from 1.172 MB to 128 MB on ESXi 6.5 and 6.0.
+     - For specific guest OS, supported minimum and maximum video memory are different, please be careful on setting this.
+   enable_3D:
+     description:
+     - Enable 3D for guest operating systems on which VMware supports 3D.
+     type: bool
+   renderer_3D:
+     description:
+     - 'If set to C(automatic), selects the appropriate option (software or hardware) for this virtual machine automatically.'
+     - 'If set to C(software), uses normal CPU processing for 3D calculations.'
+     - 'If set to C(hardware), requires graphics hardware (GPU) for faster 3D calculations.'
+     choices: [ automatic, software, hardware ]
+   memory_3D_mb:
+     description:
+     - The value of 3D Memory must be power of 2 and valid value is from 32 MB to 2048 MB.
+extends_documentation_fragment: vmware.documentation
+'''
+
+EXAMPLES = '''
+- name: Change video card settings of virtual machine
+  vmware_guest_video:
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    datacenter: "{{ datacenter_name }}"
+    validate_certs: no
+    name: test-vm
+    gather_video_facts: false
+    use_auto_detect: false
+    display_number: 2
+    video_memory_mb: 8
+    enable_3D: true
+    renderer_3D: automatic
+    memory_3D_mb: 512
+  delegate_to: localhost
+  register: video_facts
+'''
+
+RETURN = """
+video_status:
+    description: metadata about the virtual machine's video card after managing them
+    returned: always
+    type: dict
+    sample: {
+        "auto_detect": false,
+        "display_number": 2,
+        "enable_3D_support": true,
+        "memory_3D": 524288,
+        "renderer_3D": "automatic",
+        "video_memory": 8192
+    }
+"""
+
+try:
+    from pyVmomi import vim
+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, vmware_argument_spec, wait_for_task
+
+
+class PyVmomiHelper(PyVmomi):
+    def __init__(self, module):
+        super(PyVmomiHelper, self).__init__(module)
+        self.change_detected = False
+        self.config_spec = vim.vm.ConfigSpec()
+        self.config_spec.deviceChange = []
+        self.video_card_facts = None
+
+    @staticmethod
+    def is_power_of_2(num):
+        return num != 0 and ((num & (num - 1)) == 0)
+
+    def gather_video_card_facts(self, vm_obj):
+        """
+        Gather facts about VM's video card settings
+        Args:
+            vm_obj: Managed object of virtual machine
+        Returns: Video Card device and a list of dict video card configuration
+        """
+        video_facts = dict()
+        video_card = None
+        for device in vm_obj.config.hardware.device:
+            if isinstance(device, vim.vm.device.VirtualVideoCard):
+                video_card = device
+                video_facts = dict(
+                    auto_detect=device.useAutoDetect,
+                    display_number=device.numDisplays,
+                    video_memory=device.videoRamSizeInKB,
+                    enable_3D_support=device.enable3DSupport,
+                    renderer_3D=device.use3dRenderer,
+                    memory_3D=device.graphicsMemorySizeInKB,
+                )
+                break
+        return video_card, video_facts
+
+    def get_video_card_spec(self, vm_obj):
+        """
+        Get device changes of virtual machine
+        Args:
+            vm_obj: Managed object of virtual machine
+        Returns: virtual device spec
+        """
+        video_card, video_card_facts = self.gather_video_card_facts(vm_obj)
+        self.video_card_facts = video_card_facts
+        if video_card is None:
+            self.module.fail_json(msg='Not get video card device of specified virtual machine.')
+        video_spec = vim.vm.device.VirtualDeviceSpec()
+        video_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit
+        video_spec.device = video_card
+        auto_detect = False
+        enabled_3d = False
+
+        if self.params['gather_video_facts']:
+            return None
+        if self.params['use_auto_detect'] is not None:
+            if video_card_facts['auto_detect'] and self.params['use_auto_detect']:
+                auto_detect = True
+            elif not video_card_facts['auto_detect'] and self.params['use_auto_detect']:
+                video_spec.device.useAutoDetect = True
+                self.change_detected = True
+                auto_detect = True
+            elif video_card_facts['auto_detect'] and not self.params['use_auto_detect']:
+                video_spec.device.useAutoDetect = False
+                self.change_detected = True
+        else:
+            if video_card_facts['auto_detect']:
+                auto_detect = True
+        # useAutoDetect set to False then display number and video memory config can be changed
+        if not auto_detect:
+            if self.params['display_number'] is not None:
+                if self.params['display_number'] < 1:
+                    self.module.fail_json(msg="display_number attribute valid value: 1-10.")
+                if self.params['display_number'] != video_card_facts['display_number']:
+                    video_spec.device.numDisplays = self.params['display_number']
+                    self.change_detected = True
+
+            if self.params['video_memory_mb'] is not None:
+                if self.params['video_memory_mb'] < 2:
+                    self.module.fail_json(msg="video_memory_mb attribute valid value: ESXi 6.7(1.172-256 MB),"
+                                              "ESXi 6.5/6.0(1.17-128 MB).")
+                if self.params['video_memory_mb'] * 1024 != video_card_facts['video_memory']:
+                    video_spec.device.videoRamSizeInKB = self.params['video_memory_mb'] * 1024
+                    self.change_detected = True
+        else:
+            if self.params['display_number'] is not None or self.params['video_memory_mb'] is not None:
+                self.module.fail_json(msg="display_number and video_memory_mb can not be changed if use_auto_detect is true.")
+        # useAutoDetect value not control 3D config
+        if self.params['enable_3D'] is not None:
+            if self.params['enable_3D'] != video_card_facts['enable_3D_support']:
+                video_spec.device.enable3DSupport = self.params['enable_3D']
+                self.change_detected = True
+                if self.params['enable_3D']:
+                    enabled_3d = True
+            else:
+                if video_card_facts['enable_3D_support']:
+                    enabled_3d = True
+        else:
+            if video_card_facts['enable_3D_support']:
+                enabled_3d = True
+        # 3D is enabled then 3D memory and renderer method can be set
+        if enabled_3d:
+            if self.params['renderer_3D'] is not None:
+                renderer = self.params['renderer_3D'].lower()
+                if renderer not in ['automatic', 'software', 'hardware']:
+                    self.module.fail_json(msg="renderer_3D attribute valid value: automatic, software, hardware.")
+                if renderer != video_card_facts['renderer_3D']:
+                    video_spec.device.use3dRenderer = renderer
+                    self.change_detected = True
+            if self.params['memory_3D_mb'] is not None:
+                memory_3d = self.params['memory_3D_mb']
+                if not self.is_power_of_2(memory_3d):
+                    self.module.fail_json(msg="memory_3D_mb attribute should be an integer value and power of 2(32-2048).")
+                else:
+                    if memory_3d < 32 or memory_3d > 2048:
+                        self.module.fail_json(msg="memory_3D_mb attribute should be an integer value and power of 2(32-2048).")
+                if memory_3d * 1024 != video_card_facts['memory_3D']:
+                    video_spec.device.graphicsMemorySizeInKB = memory_3d * 1024
+                    self.change_detected = True
+        else:
+            if self.params['renderer_3D'] is not None or self.params['memory_3D_mb'] is not None:
+                self.module.fail_json(msg='3D renderer or 3D memory can not be configured if 3D is not enabled.')
+        if not self.change_detected:
+            return None
+        return video_spec
+
+    def reconfigure_vm_video(self, vm_obj):
+        """
+        Reconfigure video card settings of virtual machine
+        Args:
+            vm_obj: Managed object of virtual machine
+        Returns: Reconfigure results
+        """
+        video_card_spec = self.get_video_card_spec(vm_obj)
+        if video_card_spec is None:
+            return {'changed': False, 'failed': False, 'instance': self.video_card_facts}
+        self.config_spec.deviceChange.append(video_card_spec)
+        try:
+            task = vm_obj.ReconfigVM_Task(spec=self.config_spec)
+            wait_for_task(task)
+        except vim.fault.InvalidDeviceSpec as invalid_device_spec:
+            self.module.fail_json(msg="Failed to configure video card on given virtual machine due to invalid"
+                                      " device spec : %s" % (to_native(invalid_device_spec.msg)),
+                                  details="Please check ESXi server logs for more details.")
+        except vim.fault.RestrictedVersion as e:
+            self.module.fail_json(msg="Failed to reconfigure virtual machine due to"
+                                      " product versioning restrictions: %s" % to_native(e.msg))
+        if task.info.state == 'error':
+            return {'changed': self.change_detected, 'failed': True, 'msg': task.info.error.msg}
+        video_card_facts = self.gather_video_card_facts(vm_obj)[1]
+        return {'changed': self.change_detected, 'failed': False, 'instance': video_card_facts}
+
+
+def main():
+    argument_spec = vmware_argument_spec()
+    argument_spec.update(
+        name=dict(type='str'),
+        uuid=dict(type='str'),
+        folder=dict(type='str'),
+        datacenter=dict(type='str', default='ha-datacenter'),
+        gather_video_facts=dict(type='bool', default=False),
+        use_auto_detect=dict(type='bool'),
+        display_number=dict(type='int'),
+        video_memory_mb=dict(type='int'),
+        enable_3D=dict(type='bool'),
+        renderer_3D=dict(type='str', choices=['automatic', 'software', 'hardware']),
+        memory_3D_mb=dict(type='int'),
+    )
+    module = AnsibleModule(argument_spec=argument_spec,
+                           required_one_of=[['name', 'uuid']])
+    pyv = PyVmomiHelper(module)
+    vm = pyv.get_vm()
+    if not vm:
+        module.fail_json(msg='Unable to find the specified virtual machine : %s' % (module.params.get('uuid') or module.params.get('name')))
+
+    vm_facts = pyv.gather_facts(vm)
+    vm_power_state = vm_facts['hw_power_status'].lower()
+    if vm_power_state != 'poweredoff':
+        module.fail_json(msg='VM state should be poweredoff to reconfigure video card settings.')
+    result = pyv.reconfigure_vm_video(vm_obj=vm)
+    if result['failed']:
+        module.fail_json(**result)
+    else:
+        module.exit_json(**result)
+
+
+if __name__ == '__main__':
+    main()


Code review comments:
diff_hunk: @@ -0,0 +1,325 @@
+#!/usr/bin/python
+#  -*- coding: utf-8 -*-
+#  Copyright: (c) 2018, Ansible Project
+#  Copyright: (c) 2018, Diane Wang <dianew@vmware.com>
+#  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_guest_video
+short_description: Modify video card configurations of specified virtual machine in given vCenter infrastructure
+description:
+    - This module is used to reconfigure video card settings of given virtual machine.
+    - All parameters and VMware object names are case sensitive.
+version_added: '2.8'
+author:
+    - Diane Wang (@Tomorrow9) <dianew@vmware.com>
+notes:
+    - Tested on vSphere 6.0, 6.5 and 6.7
+requirements:
+    - "python >= 2.6"
+    - PyVmomi
+options:
+   name:
+     description:
+     - Name of the virtual machine.
+     - This is a required parameter, if parameter C(uuid) is not supplied.
+   uuid:
+     description:
+     - UUID of the instance to gather facts if known, this is VMware's unique identifier.
+     - This is a required parameter, if parameter C(name) is not supplied.
+   folder:
+     description:
+     - Destination folder, absolute or relative path to find an existing guest.
+     - This is a required parameter, only if multiple VMs are found with same name.
+     - The folder should include the datacenter. ESXi server's datacenter is ha-datacenter.
+     - 'Examples:'
+     - '   folder: /ha-datacenter/vm'
+     - '   folder: ha-datacenter/vm'
+     - '   folder: /datacenter1/vm'
+     - '   folder: datacenter1/vm'
+     - '   folder: /datacenter1/vm/folder1'
+     - '   folder: datacenter1/vm/folder1'
+     - '   folder: /folder1/datacenter1/vm'
+     - '   folder: folder1/datacenter1/vm'
+     - '   folder: /folder1/datacenter1/vm/folder2'
+   datacenter:
+     default: ha-datacenter
+     description:
+     - The datacenter name to which virtual machine belongs to.
+     - This parameter is case sensitive.
+   gather_video_facts:
+     description:
+     - If set to True, return settings of the video card, other attributes are ignored.
+     - If set to False, will do reconfiguration and return video card settings.
+     type: bool
+     default: 'no'
+   use_auto_detect:
+     description:
+     - 'If set to True, applies common video settings to the guest operating system, attributes C(display_number) and C(video_memory_mb) are ignored.'
+     - 'If set to False, the number of display and the total video memory will be reconfigured using C(display_number) and C(video_memory_mb).'
+     type: bool
+   display_number:
+     description:
+     - The number of display. Valid value from 1 to 10. The maximum display number is 4 on vCenter 6.0, 6.5 web UI.
+   video_memory_mb:
+     description:
+     - Valid total video memory range of virtual machine is from 1.172 MB to 256 MB on ESXi 6.7, from 1.172 MB to 128 MB on ESXi 6.5 and 6.0.
+     - For specific guest OS, supported minimum and maximum video memory are different, please be careful on setting this.
+   enable_3D:
+     description:
+     - Enable 3D for guest operating systems on which VMware supports 3D.
+     type: bool
+   renderer_3D:
+     description:
+     - 'If set to C(automatic), selects the appropriate option (software or hardware) for this virtual machine automatically.'
+     - 'If set to C(software), uses normal CPU processing for 3D calculations.'
+     - 'If set to C(hardware), requires graphics hardware (GPU) for faster 3D calculations.'
+     choices: [ automatic, software, hardware ]
+   memory_3D_mb:
+     description:
+     - The value of 3D Memory must be power of 2 and valid value is from 32 MB to 2048 MB.
+extends_documentation_fragment: vmware.documentation
+'''
+
+EXAMPLES = '''
+- name: Change video card settings of virtual machine
+  vmware_guest_video:
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    datacenter: "{{ datacenter_name }}"
+    validate_certs: no
+    name: test-vm
+    gather_video_facts: false
+    use_auto_detect: false
+    display_number: 2
+    video_memory_mb: 8
+    enable_3D: true
+    renderer_3D: automatic
+    memory_3D_mb: 512
+  delegate_to: localhost
+  register: video_facts
+'''
+
+RETURN = """
+video_status:
+    description: metadata about the virtual machine's video card after managing them
+    returned: always
+    type: dict
+    sample: {
+        "auto_detect": false,
+        "display_number": 2,
+        "enable_3D_support": true,
+        "memory_3D": 524288,
+        "renderer_3D": "automatic",
+        "video_memory": 8192
+    }
+"""
+
+try:
+    from pyVmomi import vim
+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, vmware_argument_spec, wait_for_task
+
+
+class PyVmomiHelper(PyVmomi):
+    def __init__(self, module):
+        super(PyVmomiHelper, self).__init__(module)
+        self.change_detected = False
+        self.config_spec = vim.vm.ConfigSpec()
+        self.config_spec.deviceChange = []
+        self.video_card_facts = None
+
+    @staticmethod
+    def is_power_of_2(num):
+        return num != 0 and ((num & (num - 1)) == 0)
+
+    def gather_video_card_facts(self, vm_obj):
+        """
+        Gather facts about VM's video card settings
+        Args:
+            vm_obj: Managed object of virtual machine
+        Returns: Video Card device and a list of dict video card configuration
+        """
+        video_facts = dict()
+        video_card = None
+        for device in vm_obj.config.hardware.device:
+            if isinstance(device, vim.vm.device.VirtualVideoCard):
+                video_card = device
+                video_facts = dict(
+                    auto_detect=device.useAutoDetect,
+                    display_number=device.numDisplays,
+                    video_memory=device.videoRamSizeInKB,
+                    enable_3D_support=device.enable3DSupport,
+                    renderer_3D=device.use3dRenderer,
+                    memory_3D=device.graphicsMemorySizeInKB,
+                )
+                break
+        return video_card, video_facts
+
+    def get_video_card_spec(self, vm_obj):
+        """
+        Get device changes of virtual machine
+        Args:
+            vm_obj: Managed object of virtual machine
+        Returns: virtual device spec
+        """
+        video_card, video_card_facts = self.gather_video_card_facts(vm_obj)
+        self.video_card_facts = video_card_facts
+        if video_card is None:
+            self.module.fail_json(msg='Not get video card device of specified virtual machine.')
+        video_spec = vim.vm.device.VirtualDeviceSpec()
+        video_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit
+        video_spec.device = video_card
+        auto_detect = False
+        enabled_3d = False
+
+        if self.params['gather_video_facts']:
+            return None
+        if self.params['use_auto_detect'] is not None:
+            if video_card_facts['auto_detect'] and self.params['use_auto_detect']:
+                auto_detect = True
+            elif not video_card_facts['auto_detect'] and self.params['use_auto_detect']:
+                video_spec.device.useAutoDetect = True
+                self.change_detected = True
+                auto_detect = True
+            elif video_card_facts['auto_detect'] and not self.params['use_auto_detect']:
+                video_spec.device.useAutoDetect = False
+                self.change_detected = True
+        else:
+            if video_card_facts['auto_detect']:
+                auto_detect = True
+        # useAutoDetect set to False then display number and video memory config can be changed
+        if not auto_detect:
+            if self.params['display_number'] is not None:
+                if self.params['display_number'] < 1:
+                    self.module.fail_json(msg="display_number attribute valid value: 1-10.")
+                if self.params['display_number'] != video_card_facts['display_number']:
+                    video_spec.device.numDisplays = self.params['display_number']
+                    self.change_detected = True
+
+            if self.params['video_memory_mb'] is not None:
+                if self.params['video_memory_mb'] < 2:
path: lib/ansible/modules/cloud/vmware/vmware_guest_video.py
text: @user1:
If the check is actually for 2Mb, then the message should state that, i feel (including the module documentation). Otherwise, the check should be 
```suggestion
                if float(self.params['video_memory_mb']) < 1.17:
```

@author:
The warning message from vCenter UI is "1.172 to 128 or 256", and it accepts float input on the UI, but in the background I see that is transformed to int. So here I want to accept int. I'll modify this. Thanks.

diff_hunk: @@ -0,0 +1,325 @@
+#!/usr/bin/python
+#  -*- coding: utf-8 -*-
+#  Copyright: (c) 2018, Ansible Project
+#  Copyright: (c) 2018, Diane Wang <dianew@vmware.com>
+#  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_guest_video
+short_description: Modify video card configurations of specified virtual machine in given vCenter infrastructure
+description:
+    - This module is used to reconfigure video card settings of given virtual machine.
+    - All parameters and VMware object names are case sensitive.
+version_added: '2.8'
+author:
+    - Diane Wang (@Tomorrow9) <dianew@vmware.com>
+notes:
+    - Tested on vSphere 6.0, 6.5 and 6.7
+requirements:
+    - "python >= 2.6"
+    - PyVmomi
+options:
+   name:
+     description:
+     - Name of the virtual machine.
+     - This is a required parameter, if parameter C(uuid) is not supplied.
+   uuid:
+     description:
+     - UUID of the instance to gather facts if known, this is VMware's unique identifier.
+     - This is a required parameter, if parameter C(name) is not supplied.
+   folder:
+     description:
+     - Destination folder, absolute or relative path to find an existing guest.
+     - This is a required parameter, only if multiple VMs are found with same name.
+     - The folder should include the datacenter. ESXi server's datacenter is ha-datacenter.
+     - 'Examples:'
+     - '   folder: /ha-datacenter/vm'
+     - '   folder: ha-datacenter/vm'
+     - '   folder: /datacenter1/vm'
+     - '   folder: datacenter1/vm'
+     - '   folder: /datacenter1/vm/folder1'
+     - '   folder: datacenter1/vm/folder1'
+     - '   folder: /folder1/datacenter1/vm'
+     - '   folder: folder1/datacenter1/vm'
+     - '   folder: /folder1/datacenter1/vm/folder2'
+   datacenter:
+     default: ha-datacenter
+     description:
+     - The datacenter name to which virtual machine belongs to.
+     - This parameter is case sensitive.
+   gather_video_facts:
+     description:
+     - If set to True, return settings of the video card, other attributes are ignored.
+     - If set to False, will do reconfiguration and return video card settings.
+     type: bool
+     default: 'no'
+   use_auto_detect:
+     description:
+     - 'If set to True, applies common video settings to the guest operating system, attributes C(display_number) and C(video_memory_mb) are ignored.'
+     - 'If set to False, the number of display and the total video memory will be reconfigured using C(display_number) and C(video_memory_mb).'
+     type: bool
+   display_number:
+     description:
+     - The number of display. Valid value from 1 to 10. The maximum display number is 4 on vCenter 6.0, 6.5 web UI.
+   video_memory_mb:
+     description:
+     - Valid total video memory range of virtual machine is from 1.172 MB to 256 MB on ESXi 6.7, from 1.172 MB to 128 MB on ESXi 6.5 and 6.0.
+     - For specific guest OS, supported minimum and maximum video memory are different, please be careful on setting this.
+   enable_3D:
+     description:
+     - Enable 3D for guest operating systems on which VMware supports 3D.
+     type: bool
+   renderer_3D:
+     description:
+     - 'If set to C(automatic), selects the appropriate option (software or hardware) for this virtual machine automatically.'
+     - 'If set to C(software), uses normal CPU processing for 3D calculations.'
+     - 'If set to C(hardware), requires graphics hardware (GPU) for faster 3D calculations.'
+     choices: [ automatic, software, hardware ]
+   memory_3D_mb:
+     description:
+     - The value of 3D Memory must be power of 2 and valid value is from 32 MB to 2048 MB.
+extends_documentation_fragment: vmware.documentation
+'''
+
+EXAMPLES = '''
+- name: Change video card settings of virtual machine
+  vmware_guest_video:
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    datacenter: "{{ datacenter_name }}"
+    validate_certs: no
+    name: test-vm
+    gather_video_facts: false
+    use_auto_detect: false
+    display_number: 2
+    video_memory_mb: 8
+    enable_3D: true
+    renderer_3D: automatic
+    memory_3D_mb: 512
+  delegate_to: localhost
+  register: video_facts
+'''
+
+RETURN = """
+video_status:
+    description: metadata about the virtual machine's video card after managing them
+    returned: always
+    type: dict
+    sample: {
+        "auto_detect": false,
+        "display_number": 2,
+        "enable_3D_support": true,
+        "memory_3D": 524288,
+        "renderer_3D": "automatic",
+        "video_memory": 8192
+    }
+"""
+
+try:
+    from pyVmomi import vim
+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, vmware_argument_spec, wait_for_task
+
+
+class PyVmomiHelper(PyVmomi):
+    def __init__(self, module):
+        super(PyVmomiHelper, self).__init__(module)
+        self.change_detected = False
+        self.config_spec = vim.vm.ConfigSpec()
+        self.config_spec.deviceChange = []
+        self.video_card_facts = None
+
+    @staticmethod
+    def is_power_of_2(num):
+        return num != 0 and ((num & (num - 1)) == 0)
+
+    def gather_video_card_facts(self, vm_obj):
+        """
+        Gather facts about VM's video card settings
+        Args:
+            vm_obj: Managed object of virtual machine
+        Returns: Video Card device and a list of dict video card configuration
+        """
+        video_facts = dict()
+        video_card = None
+        for device in vm_obj.config.hardware.device:
+            if isinstance(device, vim.vm.device.VirtualVideoCard):
+                video_card = device
+                video_facts = dict(
+                    auto_detect=device.useAutoDetect,
+                    display_number=device.numDisplays,
+                    video_memory=device.videoRamSizeInKB,
+                    enable_3D_support=device.enable3DSupport,
+                    renderer_3D=device.use3dRenderer,
+                    memory_3D=device.graphicsMemorySizeInKB,
+                )
+                break
+        return video_card, video_facts
+
+    def get_video_card_spec(self, vm_obj):
+        """
+        Get device changes of virtual machine
+        Args:
+            vm_obj: Managed object of virtual machine
+        Returns: virtual device spec
+        """
+        video_card, video_card_facts = self.gather_video_card_facts(vm_obj)
+        self.video_card_facts = video_card_facts
+        if video_card is None:
+            self.module.fail_json(msg='Not get video card device of specified virtual machine.')
+        video_spec = vim.vm.device.VirtualDeviceSpec()
+        video_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit
+        video_spec.device = video_card
+        auto_detect = False
+        enabled_3d = False
+
+        if self.params['gather_video_facts']:
+            return None
+        if self.params['use_auto_detect'] is not None:
+            if video_card_facts['auto_detect'] and self.params['use_auto_detect']:
+                auto_detect = True
+            elif not video_card_facts['auto_detect'] and self.params['use_auto_detect']:
+                video_spec.device.useAutoDetect = True
+                self.change_detected = True
+                auto_detect = True
+            elif video_card_facts['auto_detect'] and not self.params['use_auto_detect']:
+                video_spec.device.useAutoDetect = False
+                self.change_detected = True
+        else:
+            if video_card_facts['auto_detect']:
+                auto_detect = True
+        # useAutoDetect set to False then display number and video memory config can be changed
+        if not auto_detect:
+            if self.params['display_number'] is not None:
+                if self.params['display_number'] < 1:
+                    self.module.fail_json(msg="display_number attribute valid value: 1-10.")
+                if self.params['display_number'] != video_card_facts['display_number']:
+                    video_spec.device.numDisplays = self.params['display_number']
+                    self.change_detected = True
+
+            if self.params['video_memory_mb'] is not None:
+                if self.params['video_memory_mb'] < 2:
+                    self.module.fail_json(msg="video_memory_mb attribute valid value: ESXi 6.7(1.172-256 MB),"
+                                              "ESXi 6.5/6.0(1.17-128 MB).")
+                if self.params['video_memory_mb'] * 1024 != video_card_facts['video_memory']:
+                    video_spec.device.videoRamSizeInKB = self.params['video_memory_mb'] * 1024
+                    self.change_detected = True
+        else:
+            if self.params['display_number'] is not None or self.params['video_memory_mb'] is not None:
+                self.module.fail_json(msg="display_number and video_memory_mb can not be changed if use_auto_detect is true.")
+        # useAutoDetect value not control 3D config
+        if self.params['enable_3D'] is not None:
+            if self.params['enable_3D'] != video_card_facts['enable_3D_support']:
+                video_spec.device.enable3DSupport = self.params['enable_3D']
+                self.change_detected = True
+                if self.params['enable_3D']:
+                    enabled_3d = True
+            else:
+                if video_card_facts['enable_3D_support']:
+                    enabled_3d = True
+        else:
+            if video_card_facts['enable_3D_support']:
+                enabled_3d = True
+        # 3D is enabled then 3D memory and renderer method can be set
+        if enabled_3d:
+            if self.params['renderer_3D'] is not None:
+                renderer = self.params['renderer_3D'].lower()
+                if renderer not in ['automatic', 'software', 'hardware']:
+                    self.module.fail_json(msg="renderer_3D attribute valid value: automatic, software, hardware.")
+                if renderer != video_card_facts['renderer_3D']:
+                    video_spec.device.use3dRenderer = renderer
+                    self.change_detected = True
+            if self.params['memory_3D_mb'] is not None:
+                memory_3d = self.params['memory_3D_mb']
+                if not self.is_power_of_2(memory_3d):
+                    self.module.fail_json(msg="memory_3D_mb attribute should be an integer value and power of 2(32-2048).")
+                else:
+                    if memory_3d < 32 or memory_3d > 2048:
+                        self.module.fail_json(msg="memory_3D_mb attribute should be an integer value and power of 2(32-2048).")
+                if memory_3d * 1024 != video_card_facts['memory_3D']:
+                    video_spec.device.graphicsMemorySizeInKB = memory_3d * 1024
+                    self.change_detected = True
+        else:
+            if self.params['renderer_3D'] is not None or self.params['memory_3D_mb'] is not None:
+                self.module.fail_json(msg='3D renderer or 3D memory can not be configured if 3D is not enabled.')
+        if not self.change_detected:
+            return None
+        return video_spec
+
+    def reconfigure_vm_video(self, vm_obj):
+        """
+        Reconfigure video card settings of virtual machine
+        Args:
+            vm_obj: Managed object of virtual machine
+        Returns: Reconfigure results
+        """
+        video_card_spec = self.get_video_card_spec(vm_obj)
+        if video_card_spec is None:
+            return {'changed': False, 'failed': False, 'instance': self.video_card_facts}
+        self.config_spec.deviceChange.append(video_card_spec)
+        try:
+            task = vm_obj.ReconfigVM_Task(spec=self.config_spec)
+            wait_for_task(task)
+        except vim.fault.InvalidDeviceSpec as invalid_device_spec:
+            self.module.fail_json(msg="Failed to configure video card on given virtual machine due to invalid"
+                                      " device spec : %s" % (to_native(invalid_device_spec.msg)),
+                                  details="Please check ESXi server logs for more details.")
+        except vim.fault.RestrictedVersion as e:
+            self.module.fail_json(msg="Failed to reconfigure virtual machine due to"
+                                      " product versioning restrictions: %s" % to_native(e.msg))
+        if task.info.state == 'error':
+            return {'changed': self.change_detected, 'failed': True, 'msg': task.info.error.msg}
+        video_card_facts = self.gather_video_card_facts(vm_obj)[1]
+        return {'changed': self.change_detected, 'failed': False, 'instance': video_card_facts}
+
+
+def main():
+    argument_spec = vmware_argument_spec()
+    argument_spec.update(
+        name=dict(type='str'),
+        uuid=dict(type='str'),
+        folder=dict(type='str'),
+        datacenter=dict(type='str', default='ha-datacenter'),
+        gather_video_facts=dict(type='bool', default=False),
+        use_auto_detect=dict(type='bool'),
+        display_number=dict(type='int'),
+        video_memory_mb=dict(type='int'),
path: lib/ansible/modules/cloud/vmware/vmware_guest_video.py
text: @user1:
If the minimum value is `1.17` as per module docs, shouldn't this be a float?
```suggestion
        video_memory_mb=dict(type='float'),
```