#!/usr/bin/python
# Copyright 2016 Google Inc.
#
# 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 copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.

ANSIBLE_METADATA = {'status': ['preview'],
                    'supported_by': 'community',
                    'version': '1.0'}

DOCUMENTATION = '''
---
module: gce_mig
version_added: "2.2"
short_description: Create, Update or Destroy a Managed Instance Group (MIG).
description:
    - Create, Update or Destroy a Managed Instance Group (MIG).  See
      U(https://cloud.google.com/compute/docs/instance-groups) for an overview.
      Full install/configuration instructions for the gce* modules can
      be found in the comments of ansible/test/gce_tests.py.
requirements:
  - "python >= 2.6"
  - "apache-libcloud >= 1.2.0"
notes:
  - Resizing and Recreating VM are also supported.
  - An existing instance template is required in order to create a
    Managed Instance Group.
author:
  - "Tom Melendez (@supertom) <tom@supertom.com>"
options:
  name:
    description:
       - Name of the Managed Instance Group.
    required: true
  template:
    description:
       - Instance Template to be used in creating the VMs.  See
         U(https://cloud.google.com/compute/docs/instance-templates) to learn more
         about Instance Templates.  Required for creating MIGs.
    required: false
  size:
    description:
       - Size of Managed Instance Group.  If MIG already exists, it will be
         resized to the number provided here.  Required for creating MIGs.
    required: false
  service_account_email:
    description:
      - service account email
    required: false
    default: null
  credentials_file:
    description:
      - Path to the JSON file associated with the service account email
    default: null
    required: false
  project_id:
    description:
      - GCE project ID
    required: false
    default: null
  state:
    description:
      - desired state of the resource
    required: false
    default: "present"
    choices: ["absent", "present"]
  zone:
    description:
      - The GCE zone to use for this Managed Instance Group.
    required: true
  autoscaling:
    description:
      - A dictionary of configuration for the autoscaler. 'enabled (bool)', 'name (str)'
        and policy.max_instances (int) are required fields if autoscaling is used. See
        U(https://cloud.google.com/compute/docs/reference/beta/autoscalers) for more information
        on Autoscaling.
    required: false
    default: null
  named_ports:
    version_added: "2.3"
    description:
      - Define named ports that backend services can forward data to.  Format is a a list of
        name:port dictionaries.
    required: false
    default: null
'''

EXAMPLES = '''
# Following playbook creates, rebuilds instances, resizes and then deletes a MIG.
# Notes:
# - Two valid Instance Templates must exist in your GCE project in order to run
#   this playbook.  Change the fields to match the templates used in your
#   project.
# - The use of the 'pause' module is not required, it is just for convenience.
- name: Managed Instance Group Example
  hosts: localhost
  gather_facts: False
  tasks:
    - name: Create MIG
      gce_mig:
        name: ansible-mig-example
        zone: us-central1-c
        state: present
        size: 1
        template: my-instance-template-1
        named_ports:
        - name: http
          port: 80
        - name: foobar
          port: 82

    - name: Pause for 30 seconds
      pause:
        seconds: 30

    - name: Recreate MIG Instances with Instance Template change.
      gce_mig:
        name: ansible-mig-example
        zone: us-central1-c
        state: present
        template: my-instance-template-2-small
        recreate_instances: yes

    - name: Pause for 30 seconds
      pause:
        seconds: 30

    - name: Resize MIG
      gce_mig:
        name: ansible-mig-example
        zone: us-central1-c
        state: present
        size: 3

    - name: Update MIG with Autoscaler
      gce_mig:
        name: ansible-mig-example
        zone: us-central1-c
        state: present
        size: 3
        template: my-instance-template-2-small
        recreate_instances: yes
        autoscaling:
          enabled: yes
          name: my-autoscaler
          policy:
            min_instances: 2
            max_instances: 5
            cool_down_period: 37
            cpu_utilization:
              target: .39
            load_balancing_utilization:
              target: 0.4

    - name: Pause for 30 seconds
      pause:
        seconds: 30

    - name: Delete MIG
      gce_mig:
        name: ansible-mig-example
        zone: us-central1-c
        state: absent
        autoscaling:
          enabled: no
          name: my-autoscaler
'''
RETURN = '''
zone:
    description: Zone in which to launch MIG.
    returned: always
    type: string
    sample: "us-central1-b"

template:
    description: Instance Template to use for VMs.  Must exist prior to using with MIG.
    returned: changed
    type: string
    sample: "my-instance-template"

name:
    description: Name of the Managed Instance Group.
    returned: changed
    type: string
    sample: "my-managed-instance-group"

named_ports:
    description: list of named ports acted upon
    returned: when named_ports are initially set or updated
    type: list
    sample: [{ "name": "http", "port": 80 }, { "name": "foo", "port": 82 }]

size:
    description: Number of VMs in Managed Instance Group.
    returned: changed
    type: integer
    sample: 4

created_instances:
    description: Names of instances created.
    returned: When instances are created.
    type: list
    sample: ["ansible-mig-new-0k4y", "ansible-mig-new-0zk5", "ansible-mig-new-kp68"]

deleted_instances:
    description: Names of instances deleted.
    returned: When instances are deleted.
    type: list
    sample: ["ansible-mig-new-0k4y", "ansible-mig-new-0zk5", "ansible-mig-new-kp68"]

resize_created_instances:
    description: Names of instances created during resizing.
    returned: When a resize results in the creation of instances.
    type: list
    sample: ["ansible-mig-new-0k4y", "ansible-mig-new-0zk5", "ansible-mig-new-kp68"]

resize_deleted_instances:
    description: Names of instances deleted during resizing.
    returned: When a resize results in the deletion of instances.
    type: list
    sample: ["ansible-mig-new-0k4y", "ansible-mig-new-0zk5", "ansible-mig-new-kp68"]

recreated_instances:
    description: Names of instances recreated.
    returned: When instances are recreated.
    type: list
    sample: ["ansible-mig-new-0k4y", "ansible-mig-new-0zk5", "ansible-mig-new-kp68"]

created_autoscaler:
    description: True if Autoscaler was attempted and created.  False otherwise.
    returned: When the creation of an Autoscaler was attempted.
    type: bool
    sample: true

updated_autoscaler:
    description: True if an Autoscaler update was attempted and succeeded.
                 False returned if update failed.
    returned: When the update of an Autoscaler was attempted.
    type: bool
    sample: true

deleted_autoscaler:
    description: True if an Autoscaler delete attempted and succeeded.
                 False returned if delete failed.
    returned: When the delete of an Autoscaler was attempted.
    type: bool
    sample: true

set_named_ports:
    description: True if the named_ports have been set
    returned: named_ports have been set
    type: bool
    sample: true

updated_named_ports:
    description: True if the named_ports have been updated
    returned: named_ports have been updated
    type: bool
    sample: true
'''

import socket

try:
    import libcloud
    from libcloud.compute.types import Provider
    from libcloud.compute.providers import get_driver
    from libcloud.common.google import GoogleBaseError, QuotaExceededError, \
        ResourceExistsError, ResourceInUseError, ResourceNotFoundError
    from libcloud.compute.drivers.gce import GCEAddress
    _ = Provider.GCE
    HAS_LIBCLOUD = True
except ImportError:
    HAS_LIBCLOUD = False

try:
    from ast import literal_eval
    HAS_PYTHON26 = True
except ImportError:
    HAS_PYTHON26 = False


def _check_params(params, field_list):
    """ 
    Helper to validate params.

    Use this in function definitions if they require specific fields
    to be present.

    :param params: structure that contains the fields
    :type params: ``dict``

    :param field_list: list of dict representing the fields
                       [{'name': str, 'required': True/False', 'type': cls}]
    :type field_list: ``list`` of ``dict``

    :return True, exits otherwise
    :rtype: ``bool``
    """
    for d in field_list:
        if not d['name'] in params:
            if d['required'] is True:
                return (False, "%s is required and must be of type: %s" %
                        (d['name'], str(d['type'])))
        else:
            if not isinstance(params[d['name']], d['type']):
                return (False,
                        "%s must be of type: %s" % (d['name'], str(d['type'])))

    return (True, '')


def _validate_autoscaling_params(params):
    """ 
    Validate that the minimum configuration is present for autoscaling.

    :param params: Ansible dictionary containing autoscaling configuration
                   It is expected that autoscaling config will be found at the
                   key 'autoscaling'.
    :type  params: ``dict``

    :return: Tuple containing a boolean and a string.  True if autoscaler 
             is valid, False otherwise, plus str for message.
    :rtype: ``(``bool``, ``str``)``
    """
    if not params['autoscaling']:
        # It's optional, so if not set at all, it's valid.
        return (True, '')
    if not isinstance(params['autoscaling'], dict):
        return (False,
                'autoscaling: configuration expected to be a dictionary.')

    # check first-level required fields
    as_req_fields = [
        {'name': 'name', 'required': True, 'type': str},
        {'name': 'enabled', 'required': True, 'type': bool},
        {'name': 'policy', 'required': True, 'type': dict}
    ] # yapf: disable

    (as_req_valid, as_req_msg) = _check_params(params['autoscaling'],
                                               as_req_fields)
    if not as_req_valid:
        return (False, as_req_msg)

    # check policy configuration
    as_policy_fields = [
        {'name': 'max_instances', 'required': True, 'type': int},
        {'name': 'min_instances', 'required': False, 'type': int},
        {'name': 'cool_down_period', 'required': False, 'type': int}
    ] # yapf: disable

    (as_policy_valid, as_policy_msg) = _check_params(
        params['autoscaling']['policy'], as_policy_fields)
    if not as_policy_valid:
        return (False, as_policy_msg)

    # TODO(supertom): check utilization fields

    return (True, '')


def _validate_named_port_params(params):
    """ 
    Validate the named ports parameters

    :param params: Ansible dictionary containing named_ports configuration
                   It is expected that autoscaling config will be found at the
                   key 'named_ports'.  That key should contain a list of
                   {name : port} dictionaries.
    :type  params: ``dict``

    :return: Tuple containing a boolean and a string.  True if params
             are valid, False otherwise, plus str for message.
    :rtype: ``(``bool``, ``str``)``
    """
    if not params['named_ports']:
        # It's optional, so if not set at all, it's valid.
        return (True, '')
    if not isinstance(params['named_ports'], list):
        return (False, 'named_ports: expected list of name:port dictionaries.')
    req_fields = [
        {'name': 'name', 'required': True, 'type': str},
        {'name': 'port', 'required': True, 'type': int}
    ] # yapf: disable

    for np in params['named_ports']:
        (valid_named_ports, np_msg) = _check_params(np, req_fields)
        if not valid_named_ports:
            return (False, np_msg)

    return (True, '')


def _get_instance_list(mig, field='name', filter_list=['NONE']):
    """ 
    Helper to grab field from instances response.

    :param mig: Managed Instance Group Object from libcloud.
    :type mig:  :class: `GCEInstanceGroupManager`

    :param field: Field name in list_managed_instances response.  Defaults
                  to 'name'.
    :type  field: ``str``

    :param filter_list: list of 'currentAction' strings to filter on.  Only
                        items that match a currentAction in this list will
                        be returned.  Default is "['NONE']".
    :type  filter_list: ``list`` of ``str``

    :return: List of strings from list_managed_instances response.
    :rtype: ``list``
    """
    return [x[field] for x in mig.list_managed_instances()
            if x['currentAction'] in filter_list]


def _gen_gce_as_policy(as_params):
    """ 
    Take Autoscaler params and generate GCE-compatible policy.

    :param as_params: Dictionary in Ansible-playbook format 
                      containing policy arguments.
    :type as_params: ``dict``

    :return: GCE-compatible policy dictionary
    :rtype: ``dict``
    """
    asp_data = {}
    asp_data['maxNumReplicas'] = as_params['max_instances']
    if 'min_instances' in as_params:
        asp_data['minNumReplicas'] = as_params['min_instances']
    if 'cool_down_period' in as_params:
        asp_data['coolDownPeriodSec'] = as_params['cool_down_period']
    if 'cpu_utilization' in as_params and 'target' in as_params[
            'cpu_utilization']:
        asp_data['cpuUtilization'] = {'utilizationTarget':
                                      as_params['cpu_utilization']['target']}
    if 'load_balancing_utilization' in as_params and 'target' in as_params[
            'load_balancing_utilization']:
        asp_data['loadBalancingUtilization'] = {
            'utilizationTarget':
            as_params['load_balancing_utilization']['target']
        }

    return asp_data


def create_autoscaler(gce, mig, params):
    """
    Create a new Autoscaler for a MIG.

    :param gce: An initialized GCE driver object.
    :type gce:  :class: `GCENodeDriver`

    :param mig: An initialized GCEInstanceGroupManager.
    :type mig:  :class: `GCEInstanceGroupManager`

    :param params: Dictionary of autoscaling parameters.
    :type params:  ``dict``

    :return: Tuple with changed stats.
    :rtype: tuple in the format of (bool, list)
    """
    changed = False
    as_policy = _gen_gce_as_policy(params['policy'])
    autoscaler = gce.ex_create_autoscaler(name=params['name'], zone=mig.zone,
                                          instance_group=mig, policy=as_policy)
    if autoscaler:
        changed = True
    return changed


def update_autoscaler(gce, autoscaler, params):
    """
    Update an Autoscaler.

    Takes an existing Autoscaler object, and updates it with
    the supplied params before calling libcloud's update method.

    :param gce: An initialized GCE driver object.
    :type gce:  :class: `GCENodeDriver`

    :param autoscaler: An initialized GCEAutoscaler.
    :type  autoscaler:  :class: `GCEAutoscaler`

    :param params: Dictionary of autoscaling parameters.
    :type params:  ``dict``

    :return: True if changes, False otherwise.
    :rtype: ``bool``
    """
    as_policy = _gen_gce_as_policy(params['policy'])
    if autoscaler.policy != as_policy:
        autoscaler.policy = as_policy
        autoscaler = gce.ex_update_autoscaler(autoscaler)
        if autoscaler:
            return True
    return False


def delete_autoscaler(autoscaler):
    """
    Delete an Autoscaler.  Does not affect MIG.

    :param mig: Managed Instance Group Object from Libcloud.
    :type mig:  :class: `GCEInstanceGroupManager`

    :return: Tuple with changed stats and a list of affected instances.
    :rtype: tuple in the format of (bool, list)
    """
    changed = False
    if autoscaler.destroy():
        changed = True
    return changed


def get_autoscaler(gce, name, zone):
    """
    Get an Autoscaler from GCE.

    If the Autoscaler is not found, None is found.

    :param gce: An initialized GCE driver object.
    :type gce:  :class: `GCENodeDriver`

    :param name: Name of the Autoscaler.
    :type name:  ``str``

    :param zone: Zone that the Autoscaler is located in.
    :type zone:  ``str``

    :return: A GCEAutoscaler object or None.
    :rtype: :class: `GCEAutoscaler` or None
    """
    try:
        # Does the Autoscaler already exist?
        return gce.ex_get_autoscaler(name, zone)

    except ResourceNotFoundError:
        return None


def create_mig(gce, params):
    """
    Create a new Managed Instance Group.

    :param gce: An initialized GCE driver object.
    :type gce:  :class: `GCENodeDriver`

    :param params: Dictionary of parameters needed by the module.
    :type params:  ``dict``

    :return: Tuple with changed stats and a list of affected instances.
    :rtype: tuple in the format of (bool, list)
    """

    changed = False
    return_data = []
    actions_filter = ['CREATING']

    mig = gce.ex_create_instancegroupmanager(
        name=params['name'], size=params['size'], template=params['template'],
        zone=params['zone'])

    if mig:
        changed = True
        return_data = _get_instance_list(mig, filter_list=actions_filter)

    return (changed, return_data)


def delete_mig(mig):
    """
    Delete a Managed Instance Group.  All VMs in that MIG are also deleted."

    :param mig: Managed Instance Group Object from Libcloud.
    :type mig:  :class: `GCEInstanceGroupManager`

    :return: Tuple with changed stats and a list of affected instances.
    :rtype: tuple in the format of (bool, list)
    """
    changed = False
    return_data = []
    actions_filter = ['NONE', 'CREATING', 'RECREATING', 'DELETING',
                      'ABANDONING', 'RESTARTING', 'REFRESHING']
    instance_names = _get_instance_list(mig, filter_list=actions_filter)
    if mig.destroy():
        changed = True
        return_data = instance_names

    return (changed, return_data)


def recreate_instances_in_mig(mig):
    """
    Recreate the instances for a Managed Instance Group.

    :param mig: Managed Instance Group Object from libcloud.
    :type mig:  :class: `GCEInstanceGroupManager`

    :return: Tuple with changed stats and a list of affected instances.
    :rtype: tuple in the format of (bool, list)
    """
    changed = False
    return_data = []
    actions_filter = ['RECREATING']

    if mig.recreate_instances():
        changed = True
        return_data = _get_instance_list(mig, filter_list=actions_filter)

    return (changed, return_data)


def resize_mig(mig, size):
    """
    Resize a Managed Instance Group.

    Based on the size provided, GCE will automatically create and delete
    VMs as needed.

    :param mig: Managed Instance Group Object from libcloud.
    :type mig:  :class: `GCEInstanceGroupManager`

    :return: Tuple with changed stats and a list of affected instances.
    :rtype: tuple in the format of (bool, list)
    """
    changed = False
    return_data = []
    actions_filter = ['CREATING', 'DELETING']

    if mig.resize(size):
        changed = True
        return_data = _get_instance_list(mig, filter_list=actions_filter)

    return (changed, return_data)


def get_mig(gce, name, zone):
    """
    Get a Managed Instance Group from GCE.

    If the MIG is not found, None is found.

    :param gce: An initialized GCE driver object.
    :type gce:  :class: `GCENodeDriver`

    :param name: Name of the Managed Instance Group.
    :type name:  ``str``

    :param zone: Zone that the Managed Instance Group is located in.
    :type zone:  ``str``

    :return: A GCEInstanceGroupManager object or None.
    :rtype: :class: `GCEInstanceGroupManager` or None
    """
    try:
        # Does the MIG already exist?
        return gce.ex_get_instancegroupmanager(name=name, zone=zone)

    except ResourceNotFoundError:
        return None


def update_named_ports(mig, named_ports):
    """
    Set the named ports on a Managed Instance Group.

    Sort the existing named ports and new.  If different, update.
    This also implicitly allows for the removal of named_por

    :param mig: Managed Instance Group Object from libcloud.
    :type mig:  :class: `GCEInstanceGroupManager`

    :param named_ports: list of dictionaries in the format of {'name': port}
    :type named_ports: ``list`` of ``dict``

    :return: True if successful
    :rtype: ``bool``
    """
    changed = False
    existing_ports = []
    new_ports = []
    if hasattr(mig.instance_group, 'named_ports'):
        existing_ports = sorted(mig.instance_group.named_ports,
                                key=lambda x: x['name'])
    if named_ports is not None:
        new_ports = sorted(named_ports, key=lambda x: x['name'])

    if existing_ports != new_ports:
        if mig.instance_group.set_named_ports(named_ports):
            changed = True

    return changed


def main():
    module = AnsibleModule(argument_spec=dict(
        name=dict(required=True),
        template=dict(),
        recreate_instances=dict(type='bool', default=False),
        # Do not set a default size here.  For Create and some update
        # operations, it is required and should be explicitly set.
        # Below, we set it to the existing value if it has not been set.
        size=dict(type='int'),
        state=dict(choices=['absent', 'present'], default='present'),
        zone=dict(required=True),
        autoscaling=dict(type='dict', default=None),
        named_ports=dict(type='list', default=None),
        service_account_email=dict(),
        service_account_permissions=dict(type='list'),
        pem_file=dict(),
        credentials_file=dict(),
        project_id=dict(), ), )

    if not HAS_PYTHON26:
        module.fail_json(
            msg="GCE module requires python's 'ast' module, python v2.6+")
    if not HAS_LIBCLOUD:
        module.fail_json(
            msg='libcloud with GCE Managed Instance Group support (1.2+) required for this module.')

    gce = gce_connect(module)
    if not hasattr(gce, 'ex_create_instancegroupmanager'):
        module.fail_json(
            msg='libcloud with GCE Managed Instance Group support (1.2+) required for this module.',
            changed=False)

    params = {}
    params['state'] = module.params.get('state')
    params['zone'] = module.params.get('zone')
    params['name'] = module.params.get('name')
    params['size'] = module.params.get('size')
    params['template'] = module.params.get('template')
    params['recreate_instances'] = module.params.get('recreate_instances')
    params['autoscaling'] = module.params.get('autoscaling', None)
    params['named_ports'] = module.params.get('named_ports', None)

    (valid_autoscaling, as_msg) = _validate_autoscaling_params(params)
    if not valid_autoscaling:
        module.fail_json(msg=as_msg, changed=False)

    if params['named_ports'] is not None and not hasattr(
            gce, 'ex_instancegroup_set_named_ports'):
        module.fail_json(
            msg="Apache Libcloud 1.3.0+ is required to use 'named_ports' option",
            changed=False)

    (valid_named_ports, np_msg) = _validate_named_port_params(params)
    if not valid_named_ports:
        module.fail_json(msg=np_msg, changed=False)

    changed = False
    json_output = {'state': params['state'], 'zone': params['zone']}
    mig = get_mig(gce, params['name'], params['zone'])

    if not mig:
        if params['state'] == 'absent':
            # Doesn't exist in GCE, and state==absent.
            changed = False
            module.fail_json(
                msg="Cannot delete unknown managed instance group: %s" %
                (params['name']))
        else:
            # Create MIG
            req_create_fields = [
                {'name': 'template', 'required': True, 'type': str},
                {'name': 'size', 'required': True, 'type': int}
            ] # yapf: disable

            (valid_create_fields, valid_create_msg) = _check_params(
                params, req_create_fields)
            if not valid_create_fields:
                module.fail_json(msg=valid_create_msg, changed=False)

            (changed, json_output['created_instances']) = create_mig(gce,
                                                                     params)
            if params['autoscaling'] and params['autoscaling'][
                    'enabled'] is True:
                # Fetch newly-created MIG and create Autoscaler for it.
                mig = get_mig(gce, params['name'], params['zone'])
                if not mig:
                    module.fail_json(
                        msg='Unable to fetch created MIG %s to create \
                        autoscaler in zone: %s' % (
                            params['name'], params['zone']), changed=False)

                if not create_autoscaler(gce, mig, params['autoscaling']):
                    module.fail_json(
                        msg='Unable to fetch MIG %s to create autoscaler \
                        in zone: %s' % (params['name'], params['zone']),
                        changed=False)

                json_output['created_autoscaler'] = True
            # Add named ports if available
            if params['named_ports']:
                mig = get_mig(gce, params['name'], params['zone'])
                if not mig:
                    module.fail_json(
                        msg='Unable to fetch created MIG %s to create \
                        autoscaler in zone: %s' % (
                            params['name'], params['zone']), changed=False)
                json_output['set_named_ports'] = update_named_ports(
                    mig, params['named_ports'])
                if json_output['set_named_ports']:
                    json_output['named_ports'] = params['named_ports']

    elif params['state'] == 'absent':
        # Delete MIG

        # First, check and remove the autoscaler, if present.
        # Note: multiple autoscalers can be associated to a single MIG.  We
        # only handle the one that is named, but we might want to think about this.
        if params['autoscaling']:
            autoscaler = get_autoscaler(gce, params['autoscaling']['name'],
                                        params['zone'])
            if not autoscaler:
                module.fail_json(msg='Unable to fetch autoscaler %s to delete \
                in zone: %s' % (params['autoscaling']['name'], params['zone']),
                                 changed=False)

            changed = delete_autoscaler(autoscaler)
            json_output['deleted_autoscaler'] = changed

        # Now, delete the MIG.
        (changed, json_output['deleted_instances']) = delete_mig(mig)

    else:
        # Update MIG

        # If we're going to update a MIG, we need a size and template values.
        # If not specified, we use the values from the existing MIG.
        if not params['size']:
            params['size'] = mig.size

        if not params['template']:
            params['template'] = mig.template.name

        if params['template'] != mig.template.name:
            # Update Instance Template.
            new_template = gce.ex_get_instancetemplate(params['template'])
            mig.set_instancetemplate(new_template)
            json_output['updated_instancetemplate'] = True
            changed = True
        if params['recreate_instances'] is True:
            # Recreate Instances.
            (changed, json_output['recreated_instances']
             ) = recreate_instances_in_mig(mig)

        if params['size'] != mig.size:
            # Resize MIG.
            keystr = 'created' if params['size'] > mig.size else 'deleted'
            (changed, json_output['resize_%s_instances' %
                                  (keystr)]) = resize_mig(mig, params['size'])

        # Update Autoscaler
        if params['autoscaling']:
            autoscaler = get_autoscaler(gce, params['autoscaling']['name'],
                                        params['zone'])
            if not autoscaler:
                # Try to create autoscaler.
                # Note: this isn't perfect, if the autoscaler name has changed
                # we wouldn't know that here.
                if not create_autoscaler(gce, mig, params['autoscaling']):
                    module.fail_json(
                        msg='Unable to create autoscaler %s for existing MIG %s\
                        in zone: %s' % (params['autoscaling']['name'],
                                        params['name'], params['zone']),
                        changed=False)
                json_output['created_autoscaler'] = True
                changed = True
            else:
                if params['autoscaling']['enabled'] is False:
                    # Delete autoscaler
                    changed = delete_autoscaler(autoscaler)
                    json_output['delete_autoscaler'] = changed
                else:
                    # Update policy, etc.
                    changed = update_autoscaler(gce, autoscaler,
                                                params['autoscaling'])
                    json_output['updated_autoscaler'] = changed
        named_ports = params['named_ports'] or []
        json_output['updated_named_ports'] = update_named_ports(mig,
                                                                named_ports)
        if json_output['updated_named_ports']:
            json_output['named_ports'] = named_ports

    json_output['changed'] = changed
    json_output.update(params)
    module.exit_json(**json_output)

# import module snippets
from ansible.module_utils.basic import *
from ansible.module_utils.gce import *
if __name__ == '__main__':
    main()
