#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: (c) 2017, Abhijeet Kasurde <akasurde@redhat.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 = r'''
---
module: vmware_guest_powerstate
short_description: Manages power states of virtual machines in vCenter
description:
- Power on / Power off / Restart a virtual machine.
version_added: '2.5'
author:
- Abhijeet Kasurde (@Akasurde) <akasurde@redhat.com>
requirements:
- python >= 2.6
- PyVmomi
options:
  state:
    description:
    - Set the state of the virtual machine.
    choices: [ powered-off, powered-on, reboot-guest, restarted, shutdown-guest, suspended, present]
    default: present
    type: str
  name:
    description:
    - Name of the virtual machine to work with.
    - Virtual machine names in vCenter are not necessarily unique, which may be problematic, see C(name_match).
    type: str
  name_match:
    description:
    - If multiple virtual machines matching the name, use the first or last found.
    default: first
    choices: [ first, last ]
    type: str
  uuid:
    description:
    - UUID of the instance to manage if known, this is VMware's unique identifier.
    - This is required if C(name) or C(moid) is not supplied.
    type: str
  moid:
    description:
    - Managed Object ID of the instance to manage if known, this is a unique identifier only within a single vCenter instance.
    - This is required if C(name) or C(uuid) is not supplied.
    version_added: '2.9'
    type: str
  use_instance_uuid:
    description:
    - Whether to use the VMware instance UUID rather than the BIOS UUID.
    default: no
    type: bool
    version_added: '2.8'
  folder:
    description:
    - Destination folder, absolute or relative path to find an existing guest.
    - The folder should include the datacenter. ESX'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'
    type: str
  scheduled_at:
    description:
    - Date and time in string format at which specified task needs to be performed.
    - "The required format for date and time - 'dd/mm/yyyy hh:mm'."
    - Scheduling task requires vCenter server. A standalone ESXi server does not support this option.
    type: str
  schedule_task_name:
    description:
    - Name of schedule task.
    - Valid only if C(scheduled_at) is specified.
    type: str
    required: False
    version_added: '2.9'
  schedule_task_description:
    description:
    - Description of schedule task.
    - Valid only if C(scheduled_at) is specified.
    type: str
    required: False
    version_added: '2.9'
  schedule_task_enabled:
    description:
    - Flag to indicate whether the scheduled task is enabled or disabled.
    type: bool
    required: False
    default: True
    version_added: '2.9'
  force:
    description:
    - Ignore warnings and complete the actions.
    - This parameter is useful while forcing virtual machine state.
    default: False
    type: bool
    version_added: 2.5
  state_change_timeout:
    description:
    - If the C(state) is set to C(shutdown-guest), by default the module will return immediately after sending the shutdown signal.
    - If this argument is set to a positive integer, the module will instead wait for the VM to reach the poweredoff state.
    - The value sets a timeout in seconds for the module to wait for the state change.
    default: 0
    version_added: '2.6'
    type: int
extends_documentation_fragment: vmware.documentation
'''

EXAMPLES = r'''
- name: Set the state of a virtual machine to poweroff
  vmware_guest_powerstate:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: no
    folder: "/{{ datacenter_name }}/vm/my_folder"
    name: "{{ guest_name }}"
    state: powered-off
  delegate_to: localhost
  register: deploy

- name: Set the state of a virtual machine to poweron using MoID
  vmware_guest_powerstate:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: no
    folder: "/{{ datacenter_name }}/vm/my_folder"
    moid: vm-42
    state: powered-on
  delegate_to: localhost
  register: deploy

- name: Set the state of a virtual machine to poweroff at given scheduled time
  vmware_guest_powerstate:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    folder: "/{{ datacenter_name }}/vm/my_folder"
    name: "{{ guest_name }}"
    state: powered-off
    scheduled_at: "09/01/2018 10:18"
    schedule_task_name: "task_00001"
    schedule_task_description: "Sample task to poweroff VM"
    schedule_task_enabled: True
  delegate_to: localhost
  register: deploy_at_schedule_datetime

- name: Wait for the virtual machine to shutdown
  vmware_guest_powerstate:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    name: "{{ guest_name }}"
    state: shutdown-guest
    state_change_timeout: 200
  delegate_to: localhost
  register: deploy
'''

RETURN = r''' # '''

try:
    from pyVmomi import vim, vmodl
except ImportError:
    pass

from random import randint
from datetime import datetime
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.vmware import PyVmomi, set_vm_power_state, vmware_argument_spec
from ansible.module_utils._text import to_native


def main():
    argument_spec = vmware_argument_spec()
    argument_spec.update(
        state=dict(type='str', default='present',
                   choices=['present', 'powered-off', 'powered-on', 'reboot-guest', 'restarted', 'shutdown-guest', 'suspended']),
        name=dict(type='str'),
        name_match=dict(type='str', choices=['first', 'last'], default='first'),
        uuid=dict(type='str'),
        moid=dict(type='str'),
        use_instance_uuid=dict(type='bool', default=False),
        folder=dict(type='str'),
        force=dict(type='bool', default=False),
        scheduled_at=dict(type='str'),
        schedule_task_name=dict(),
        schedule_task_description=dict(),
        schedule_task_enabled=dict(type='bool', default=True),
        state_change_timeout=dict(type='int', default=0),
    )

    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=False,
        mutually_exclusive=[
            ['name', 'uuid', 'moid'],
        ],
    )

    result = dict(changed=False,)

    pyv = PyVmomi(module)

    # Check if the VM exists before continuing
    vm = pyv.get_vm()

    if vm:
        # VM already exists, so set power state
        scheduled_at = module.params.get('scheduled_at', None)
        if scheduled_at:
            if not pyv.is_vcenter():
                module.fail_json(msg="Scheduling task requires vCenter, hostname %s "
                                     "is an ESXi server." % module.params.get('hostname'))
            powerstate = {
                'present': vim.VirtualMachine.PowerOn,
                'powered-off': vim.VirtualMachine.PowerOff,
                'powered-on': vim.VirtualMachine.PowerOn,
                'reboot-guest': vim.VirtualMachine.RebootGuest,
                'restarted': vim.VirtualMachine.Reset,
                'shutdown-guest': vim.VirtualMachine.ShutdownGuest,
                'suspended': vim.VirtualMachine.Suspend,
            }
            dt = ''
            try:
                dt = datetime.strptime(scheduled_at, '%d/%m/%Y %H:%M')
            except ValueError as e:
                module.fail_json(msg="Failed to convert given date and time string to Python datetime object,"
                                     "please specify string in 'dd/mm/yyyy hh:mm' format: %s" % to_native(e))
            schedule_task_spec = vim.scheduler.ScheduledTaskSpec()
            schedule_task_name = module.params['schedule_task_name'] or 'task_%s' % str(randint(10000, 99999))
            schedule_task_desc = module.params['schedule_task_description']
            if schedule_task_desc is None:
                schedule_task_desc = 'Schedule task for vm %s for ' \
                                     'operation %s at %s' % (vm.name, module.params['state'], scheduled_at)
            schedule_task_spec.name = schedule_task_name
            schedule_task_spec.description = schedule_task_desc
            schedule_task_spec.scheduler = vim.scheduler.OnceTaskScheduler()
            schedule_task_spec.scheduler.runAt = dt
            schedule_task_spec.action = vim.action.MethodAction()
            schedule_task_spec.action.name = powerstate[module.params['state']]
            schedule_task_spec.enabled = module.params['schedule_task_enabled']

            try:
                pyv.content.scheduledTaskManager.CreateScheduledTask(vm, schedule_task_spec)
                # As this is async task, we create scheduled task and mark state to changed.
                module.exit_json(changed=True)
            except vim.fault.InvalidName as e:
                module.fail_json(msg="Failed to create scheduled task %s for %s : %s" % (module.params.get('state'),
                                                                                         vm.name,
                                                                                         to_native(e.msg)))
            except vim.fault.DuplicateName as e:
                module.exit_json(changed=False, details=to_native(e.msg))
            except vmodl.fault.InvalidArgument as e:
                module.fail_json(msg="Failed to create scheduled task %s as specifications "
                                     "given are invalid: %s" % (module.params.get('state'),
                                                                to_native(e.msg)))
        else:
            result = set_vm_power_state(pyv.content, vm, module.params['state'], module.params['force'], module.params['state_change_timeout'])
    else:
        id = module.params.get('uuid') or module.params.get('moid') or module.params.get('name')
        module.fail_json(msg="Unable to set power state for non-existing virtual machine : '%s'" % id)

    if result.get('failed') is True:
        module.fail_json(**result)

    module.exit_json(**result)


if __name__ == '__main__':
    main()
