#!/usr/bin/python
# -*- coding: utf-8 -*-

# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

"""
(c) 2018, Milan Ilic <milani@nordeus.com>

This file is part of Ansible

Ansible is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Ansible is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a clone of the GNU General Public License
along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
"""

DOCUMENTATION = '''
---
module: one_image_info
short_description: Gather information on OpenNebula images
description:
  - Gather information on OpenNebula images.
  - This module was called C(one_image_facts) before Ansible 2.9. The usage did not change.
requirements:
  - pyone
options:
  api_url:
    description:
      - URL of the OpenNebula RPC server.
      - It is recommended to use HTTPS so that the username/password are not
      - transferred over the network unencrypted.
      - If not set then the value of the C(ONE_URL) environment variable is used.
    type: str
  api_username:
    description:
      - Name of the user to login into the OpenNebula RPC server. If not set
      - then the value of the C(ONE_USERNAME) environment variable is used.
    type: str
  api_password:
    description:
      - Password of the user to login into OpenNebula RPC server. If not set
      - then the value of the C(ONE_PASSWORD) environment variable is used.
    type: str
  ids:
    description:
      - A list of images ids whose facts you want to gather.
    aliases: ['id']
    type: list
    elements: str
  name:
    description:
      - A C(name) of the image whose facts will be gathered.
      - If the C(name) begins with '~' the C(name) will be used as regex pattern
      - which restricts the list of images (whose facts will be returned) whose names match specified regex.
      - Also, if the C(name) begins with '~*' case-insensitive matching will be performed.
      - See examples for more details.
    type: str
author:
    - "Milan Ilic (@ilicmilan)"
    - "Jan Meerkamp (@meerkampdvv)"
'''

EXAMPLES = '''
- name: Gather facts about all images
  community.general.one_image_info:
  register: result

- name: Print all images facts
  ansible.builtin.debug:
    msg: result

- name: Gather facts about an image using ID
  community.general.one_image_info:
    ids:
      - 123

- name: Gather facts about an image using the name
  community.general.one_image_info:
    name: 'foo-image'
  register: foo_image

- name: Gather facts about all IMAGEs whose name matches regex 'app-image-.*'
  community.general.one_image_info:
    name: '~app-image-.*'
  register: app_images

- name: Gather facts about all IMAGEs whose name matches regex 'foo-image-.*' ignoring cases
  community.general.one_image_info:
    name: '~*foo-image-.*'
  register: foo_images
'''

RETURN = '''
images:
    description: A list of images info
    type: complex
    returned: success
    contains:
        id:
            description: image id
            type: int
            sample: 153
        name:
            description: image name
            type: str
            sample: app1
        group_id:
            description: image's group id
            type: int
            sample: 1
        group_name:
            description: image's group name
            type: str
            sample: one-users
        owner_id:
            description: image's owner id
            type: int
            sample: 143
        owner_name:
            description: image's owner name
            type: str
            sample: ansible-test
        state:
            description: state of image instance
            type: str
            sample: READY
        used:
            description: is image in use
            type: bool
            sample: true
        running_vms:
            description: count of running vms that use this image
            type: int
            sample: 7
'''

try:
    import pyone
    HAS_PYONE = True
except ImportError:
    HAS_PYONE = False

from ansible.module_utils.basic import AnsibleModule
import os


def get_all_images(client):
    pool = client.imagepool.info(-2, -1, -1, -1)
    # Filter -2 means fetch all images user can Use

    return pool


IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']


def get_image_info(image):
    info = {
        'id': image.ID,
        'name': image.NAME,
        'state': IMAGE_STATES[image.STATE],
        'running_vms': image.RUNNING_VMS,
        'used': bool(image.RUNNING_VMS),
        'user_name': image.UNAME,
        'user_id': image.UID,
        'group_name': image.GNAME,
        'group_id': image.GID,
    }
    return info


def get_images_by_ids(module, client, ids):
    images = []
    pool = get_all_images(client)

    for image in pool.IMAGE:
        if str(image.ID) in ids:
            images.append(image)
            ids.remove(str(image.ID))
            if len(ids) == 0:
                break

    if len(ids) > 0:
        module.fail_json(msg='There is no IMAGE(s) with id(s)=' + ', '.join('{id}'.format(id=str(image_id)) for image_id in ids))

    return images


def get_images_by_name(module, client, name_pattern):

    images = []
    pattern = None

    pool = get_all_images(client)

    if name_pattern.startswith('~'):
        import re
        if name_pattern[1] == '*':
            pattern = re.compile(name_pattern[2:], re.IGNORECASE)
        else:
            pattern = re.compile(name_pattern[1:])

    for image in pool.IMAGE:
        if pattern is not None:
            if pattern.match(image.NAME):
                images.append(image)
        elif name_pattern == image.NAME:
            images.append(image)
            break

    # if the specific name is indicated
    if pattern is None and len(images) == 0:
        module.fail_json(msg="There is no IMAGE with name=" + name_pattern)

    return images


def get_connection_info(module):

    url = module.params.get('api_url')
    username = module.params.get('api_username')
    password = module.params.get('api_password')

    if not url:
        url = os.environ.get('ONE_URL')

    if not username:
        username = os.environ.get('ONE_USERNAME')

    if not password:
        password = os.environ.get('ONE_PASSWORD')

    if not(url and username and password):
        module.fail_json(msg="One or more connection parameters (api_url, api_username, api_password) were not specified")
    from collections import namedtuple

    auth_params = namedtuple('auth', ('url', 'username', 'password'))

    return auth_params(url=url, username=username, password=password)


def main():
    fields = {
        "api_url": {"required": False, "type": "str"},
        "api_username": {"required": False, "type": "str"},
        "api_password": {"required": False, "type": "str", "no_log": True},
        "ids": {"required": False, "aliases": ['id'], "type": "list", "elements": "str"},
        "name": {"required": False, "type": "str"},
    }

    module = AnsibleModule(argument_spec=fields,
                           mutually_exclusive=[['ids', 'name']],
                           supports_check_mode=True)

    if not HAS_PYONE:
        module.fail_json(msg='This module requires pyone to work!')

    auth = get_connection_info(module)
    params = module.params
    ids = params.get('ids')
    name = params.get('name')
    client = pyone.OneServer(auth.url, session=auth.username + ':' + auth.password)

    if ids:
        images = get_images_by_ids(module, client, ids)
    elif name:
        images = get_images_by_name(module, client, name)
    else:
        images = get_all_images(client).IMAGE

    result = {
        'images': [get_image_info(image) for image in images],
    }

    module.exit_json(**result)


if __name__ == '__main__':
    main()