#!/usr/bin/python
# Copyright: Ansible Project
# 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: sl_vm
short_description: create or cancel a virtual instance in SoftLayer
description:
  - Creates or cancels SoftLayer instances. When created, optionally waits for it to be 'running'.
version_added: "2.1"
options:
  instance_id:
    description:
      - Instance Id of the virtual instance to perform action option
    required: false
    default: null
  hostname:
    description:
      - Hostname to be provided to a virtual instance
    required: false
    default: null
  domain:
    description:
      - Domain name to be provided to a virtual instance
    required: false
    default: null
  datacenter:
    description:
      - Datacenter for the virtual instance to be deployed
    required: false
    default: null
  tags:
    description:
      - Tag or list of tags to be provided to a virtual instance
    required: false
    default: null
  hourly:
    description:
      - Flag to determine if the instance should be hourly billed
    required: false
    default: true
  private:
    description:
      - Flag to determine if the instance should be private only
    required: false
    default: false
  dedicated:
    description:
      - Flag to determine if the instance should be deployed in dedicated space
    required: false
    default: false
  local_disk:
    description:
      - Flag to determine if local disk should be used for the new instance
    required: false
    default: true
  cpus:
    description:
      - Count of cpus to be assigned to new virtual instance
    required: true
    default: null
  memory:
    description:
      - Amount of memory to be assigned to new virtual instance
    required: true
    default: null
  disks:
    description:
      - List of disk sizes to be assigned to new virtual instance
    required: true
    default: [25]
  os_code:
    description:
      - OS Code to be used for new virtual instance
    required: false
    default: null
  image_id:
    description:
      - Image Template to be used for new virtual instance
    required: false
    default: null
  nic_speed:
    description:
      - NIC Speed to be assigned to new virtual instance
    required: false
    default: 10
  public_vlan:
    description:
      - VLAN by its Id to be assigned to the public NIC
    required: false
    default: null
  private_vlan:
    description:
      - VLAN by its Id to be assigned to the private NIC
    required: false
    default: null
  ssh_keys:
    description:
      - List of ssh keys by their Id to be assigned to a virtual instance
    required: false
    default: null
  post_uri:
    description:
      - URL of a post provisioning script to be loaded and executed on virtual instance
    required: false
    default: null
  state:
    description:
      - Create, or cancel a virtual instance. Specify "present" for create, "absent" to cancel.
    required: false
    default: 'present'
  wait:
    description:
      - Flag used to wait for active status before returning
    required: false
    default: true
  wait_time:
    description:
      - time in seconds before wait returns
    required: false
    default: 600

requirements:
    - "python >= 2.6"
    - "softlayer >= 4.1.1"
author: "Matt Colton (@mcltn)"
'''

EXAMPLES = '''
- name: Build instance
  hosts: localhost
  gather_facts: False
  tasks:
  - name: Build instance request
    sl_vm:
      hostname: instance-1
      domain: anydomain.com
      datacenter: dal09
      tags: ansible-module-test
      hourly: True
      private: False
      dedicated: False
      local_disk: True
      cpus: 1
      memory: 1024
      disks: [25]
      os_code: UBUNTU_LATEST
      wait: False

- name: Build additional instances
  hosts: localhost
  gather_facts: False
  tasks:
  - name: Build instances request
    sl_vm:
      hostname: "{{ item.hostname }}"
      domain: "{{ item.domain }}"
      datacenter: "{{ item.datacenter }}"
      tags: "{{ item.tags }}"
      hourly: "{{ item.hourly }}"
      private: "{{ item.private }}"
      dedicated: "{{ item.dedicated }}"
      local_disk: "{{ item.local_disk }}"
      cpus: "{{ item.cpus }}"
      memory: "{{ item.memory }}"
      disks: "{{ item.disks }}"
      os_code: "{{ item.os_code }}"
      ssh_keys: "{{ item.ssh_keys }}"
      wait: "{{ item.wait }}"
    with_items:
      - hostname: instance-2
        domain: anydomain.com
        datacenter: dal09
        tags:
          - ansible-module-test
          - ansible-module-test-slaves
        hourly: True
        private: False
        dedicated: False
        local_disk: True
        cpus: 1
        memory: 1024
        disks:
          - 25
          - 100
        os_code: UBUNTU_LATEST
        ssh_keys: []
        wait: True
      - hostname: instance-3
        domain: anydomain.com
        datacenter: dal09
        tags:
          - ansible-module-test
          - ansible-module-test-slaves
        hourly: True
        private: False
        dedicated: False
        local_disk: True
        cpus: 1
        memory: 1024
        disks:
          - 25
          - 100
        os_code: UBUNTU_LATEST
        ssh_keys: []
        wait: True

- name: Cancel instances
  hosts: localhost
  gather_facts: False
  tasks:
  - name: Cancel by tag
    sl_vm:
      state: absent
      tags: ansible-module-test
'''

# TODO: Disabled RETURN as it is breaking the build for docs. Needs to be fixed.
RETURN = '''# '''

import json
import time

try:
    import SoftLayer
    from SoftLayer import VSManager

    HAS_SL = True
    vsManager = VSManager(SoftLayer.create_client_from_env())
except ImportError:
    HAS_SL = False

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import string_types


#TODO: get this info from API
STATES = ['present', 'absent']
DATACENTERS = ['ams01', 'ams03', 'che01', 'dal01', 'dal05', 'dal06', 'dal09', 'dal10', 'fra02', 'hkg02', 'hou02', 'lon02', 'mel01', 'mex01', 'mil01', 'mon01',
               'osl01', 'par01', 'sjc01', 'sjc03', 'sao01', 'sea01', 'sng01', 'syd01', 'tok02', 'tor01', 'wdc01', 'wdc04']
CPU_SIZES = [1, 2, 4, 8, 16, 32, 56]
MEMORY_SIZES = [1024, 2048, 4096, 6144, 8192, 12288, 16384, 32768, 49152, 65536, 131072, 247808]
INITIALDISK_SIZES = [25, 100]
LOCALDISK_SIZES = [25, 100, 150, 200, 300]
SANDISK_SIZES = [10, 20, 25, 30, 40, 50, 75, 100, 125, 150, 175, 200, 250, 300, 350, 400, 500, 750, 1000, 1500, 2000]
NIC_SPEEDS = [10, 100, 1000]


def create_virtual_instance(module):

    instances = vsManager.list_instances(
        hostname = module.params.get('hostname'),
        domain = module.params.get('domain'),
        datacenter = module.params.get('datacenter')
    )

    if instances:
        return False, None


    # Check if OS or Image Template is provided (Can't be both, defaults to OS)
    if (module.params.get('os_code') is not None and module.params.get('os_code') != ''):
        module.params['image_id'] = ''
    elif (module.params.get('image_id') is not None and module.params.get('image_id') != ''):
        module.params['os_code'] = ''
        module.params['disks'] = [] # Blank out disks since it will use the template
    else:
        return False, None

    tags = module.params.get('tags')
    if isinstance(tags, list):
        tags = ','.join(map(str, module.params.get('tags')))

    instance = vsManager.create_instance(
        hostname = module.params.get('hostname'),
        domain = module.params.get('domain'),
        cpus = module.params.get('cpus'),
        memory = module.params.get('memory'),
        hourly = module.params.get('hourly'),
        datacenter = module.params.get('datacenter'),
        os_code = module.params.get('os_code'),
        image_id = module.params.get('image_id'),
        local_disk = module.params.get('local_disk'),
        disks = module.params.get('disks'),
        ssh_keys = module.params.get('ssh_keys'),
        nic_speed = module.params.get('nic_speed'),
        private = module.params.get('private'),
        public_vlan = module.params.get('public_vlan'),
        private_vlan = module.params.get('private_vlan'),
        dedicated = module.params.get('dedicated'),
        post_uri = module.params.get('post_uri'),
        tags = tags)

    if instance is not None and instance['id'] > 0:
        return True, instance
    else:
        return False, None


def wait_for_instance(module,id):
    instance = None
    completed = False
    wait_timeout = time.time() + module.params.get('wait_time')
    while not completed and wait_timeout > time.time():
        try:
            completed = vsManager.wait_for_ready(id, 10, 2)
            if completed:
                instance = vsManager.get_instance(id)
        except:
            completed = False

    return completed, instance


def cancel_instance(module):
    canceled = True
    if module.params.get('instance_id') is None and (module.params.get('tags') or module.params.get('hostname') or module.params.get('domain')):
        tags = module.params.get('tags')
        if isinstance(tags, string_types):
            tags = [module.params.get('tags')]
        instances = vsManager.list_instances(tags = tags, hostname = module.params.get('hostname'), domain = module.params.get('domain'))
        for instance in instances:
            try:
                vsManager.cancel_instance(instance['id'])
            except:
                canceled = False
    elif module.params.get('instance_id') and module.params.get('instance_id') != 0:
        try:
            vsManager.cancel_instance(instance['id'])
        except:
            canceled = False
    else:
        return False, None

    return canceled, None


def main():

    module = AnsibleModule(
        argument_spec=dict(
            instance_id=dict(),
            hostname=dict(),
            domain=dict(),
            datacenter=dict(choices=DATACENTERS),
            tags=dict(),
            hourly=dict(type='bool', default=True),
            private=dict(type='bool', default=False),
            dedicated=dict(type='bool', default=False),
            local_disk=dict(type='bool', default=True),
            cpus=dict(type='int', choices=CPU_SIZES),
            memory=dict(type='int', choices=MEMORY_SIZES),
            disks=dict(type='list', default=[25]),
            os_code=dict(),
            image_id=dict(),
            nic_speed=dict(type='int', choices=NIC_SPEEDS),
            public_vlan=dict(),
            private_vlan=dict(),
            ssh_keys=dict(type='list', default=[]),
            post_uri=dict(),
            state=dict(default='present', choices=STATES),
            wait=dict(type='bool', default=True),
            wait_time=dict(type='int', default=600)
            )
    )

    if not HAS_SL:
        module.fail_json(msg='softlayer python library required for this module')

    if module.params.get('state') == 'absent':
        (changed, instance) = cancel_instance(module)

    elif module.params.get('state') == 'present':
        (changed, instance) = create_virtual_instance(module)
        if module.params.get('wait') is True and instance:
            (changed, instance) = wait_for_instance(module, instance['id'])

    module.exit_json(changed=changed, instance=json.loads(json.dumps(instance, default=lambda o: o.__dict__)))


if __name__ == '__main__':
    main()
