text: #!/usr/bin/python
  # -*- coding: utf-8 -*-
  
  # Copyright: (c) 2020, Rhys Campbell (@rhysmeister) <rhys.james.campbell@googlemail.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
  
  
  DOCUMENTATION = r'''
  ---
  module: mongodb_index
  
  short_description: Creates or drops indexes on MongoDB collections.
  
  description:
    - Creates or drops indexes on MongoDB collections.
    - Supports multiple index options, i.e. unique, sparse and partial.
    - Validates existence of indexes by name only.
  
  author: Rhys Campbell (@rhysmeister)
  version_added: "1.0.0"
  
  extends_documentation_fragment:
    - community.mongodb.login_options
    - community.mongodb.ssl_options
  
  options:
    indexes:
      description:
        - List of indexes to create or drop
      type: list
      elements: raw
      required: yes
    replica_set:
      description:
        - Replica set to connect to (automatically connects to primary for writes).
      type: str
  notes:
      - Requires the pymongo Python package on the remote host, version 2.4.2+.
  
  requirements:
    - pymongo
  '''
  
  EXAMPLES = r'''
  - name: Create a single index on a collection
    community.mongodb.mongodb_index:
      login_user: admin
      login_password: secret
      indexes:
        - database: mydb
          collection: test
          keys:
            - username: 1
              last_login: -1
          options:
            name: myindex
          state: present
  
  - name: Drop an index on a collection
    community.mongodb.mongodb_index:
      login_user: admin
      login_password: secret
      indexes:
        - database: mydb
          collection: test
          options:
            name: myindex
          state: absent
  
  - name: Create multiple indexes
    community.mongodb.mongodb_index:
      login_user: admin
      login_password: secret
      indexes:
        - database: mydb
          collection: test
          keys:
            - username: 1
              last_login: -1
          options:
            name: myindex
          state: present
        - database: mydb
          collection: test
          keys:
            - email: 1
              last_login: -1
          options:
            name: myindex2
          state: present
  
  - name: Add a unique index
    community.mongodb.mongodb_index:
      login_port: 27017
      login_user: admin
      login_password: secret
      login_database: "admin"
      indexes:
        - database: "test"
          collection: "rhys"
          keys:
            username: 1
          options:
            name: myuniqueindex
            unique: true
          state: present
  
  - name: Add a ttl index
    community.mongodb.mongodb_index:
      login_port: 27017
      login_user: admin
      login_password: secret
      login_database: "admin"
      indexes:
        - database: "test"
          collection: "rhys"
          keys:
            created: 1
          options:
            name: myttlindex
            expireAfterSeconds: 3600
          state: present
  
  - name: Add a sparse index
    community.mongodb.mongodb_index:
      login_port: 27017
      login_user: admin
      login_password: secret
      login_database: "admin"
      indexes:
        - database: "test"
          collection: "rhys"
          keys:
            last_login: -1
          options:
            name: mysparseindex
            sparse: true
          state: present
  
  - name: Add a partial index
    community.mongodb.mongodb_index:
      login_port: 27017
      login_user: admin
      login_password: secret
      login_database: "admin"
      indexes:
        - database: "test"
          collection: "rhys"
          keys:
            last_login: -1
          options:
            name: mypartialindex
            partialFilterExpression:
              rating:
                $gt: 5
          state: present
  
  - name: Add a index in the background (background option is deprecated from 4.2+)
    community.mongodb.mongodb_index:
      login_port: 27017
      login_user: admin
      login_password: secret
      login_database: "admin"
      indexes:
        - database: "test"
          collection: "rhys"
          options:
            name: idxbackground
          keys:
            username: -1
          backgroud: true
          state: present
  
  - name: Check creating 5 index all with multiple options specified
    community.mongodb.mongodb_index:
      login_port: 27017
      login_user: admin
      login_password: secret
      login_database: "admin"
      indexes:
        - database: "test"
          collection: "indextest"
          options:
            name: "idx_unq_username"
            unique: true
          keys:
            username: -1
          state: present
        - database: "test"
          collection: "indextest"
          options:
            name: "idx_last_login"
            sparse: true
          keys:
            last_login: -1
          state: present
        - database: "test"
          collection: "indextest"
          options:
            name: "myindex"
          keys:
            first_name: 1
            last_name: -1
            city: 1
          state: present
        - database: "test"
          collection: partialtest
          options:
            name: "idx_partialtest"
            partialFilterExpression:
              rating:
                $gt: 5
          keys:
            rating: -1
            title: 1
          state: present
        - database: "test"
          collection: "wideindex"
          options:
            name: "mywideindex"
          keys:
            email: -1
            username: 1
            first_name: 1
            last_name: 1
            dob: -1
            city: 1
            last_login: -1
            review_count: 1
            rating_count: 1
            last_post: -1
          state: present
  '''
  
  RETURN = r'''
  indexes_created:
    description: List of indexes created.
    returned: always
    type: list
    sample: ["myindex", "myindex2"]
  indexes_dropped:
    description: List of indexes dropped.
    returned: always
    type: list
    sample: ["myindex", "myindex2"]
  changed:
    description: Indicates the module has changed something.
    returned: When the module has changed something.
    type: bool
  failed:
    description: Indicates the module has failed.
    returned: When the module has encountered an error.
    type: bool
  '''
  
  from ansible.module_utils.basic import AnsibleModule
  from ansible_collections.community.mongodb.plugins.module_utils.mongodb_common import (
      missing_required_lib,
      mongodb_common_argument_spec,
      ssl_connection_options
  )
  from ansible_collections.community.mongodb.plugins.module_utils.mongodb_common import (
      PYMONGO_IMP_ERR,
      pymongo_found,
      MongoClient
  )
  from ansible_collections.community.mongodb.plugins.module_utils.mongodb_common import (
      index_exists,
      create_index,
      drop_index,
      mongo_auth
  )
  
  
  def validate_module(module):
      '''
      Runs validation rules specific the mongodb_index module
      '''
      required_index_keys = [
          "database",
          "collection",
          "options",
          "state",
      ]
      indexes = module.params['indexes']
  
      if len(indexes) == 0:
          module.fail_json(msg="One or more indexes must be specified")
      if not all(isinstance(i, dict) for i in indexes):
          module.fail_json(msg="Indexes must be supplied as dictionaries")
  
      # Ensure keys are present in index spec
      for k in required_index_keys:
          for i in indexes:
              if k not in i.keys():
                  module.fail_json(msg="Missing required index key {0}".format(k))
  
      # Check index subkeys look correct
      for i in indexes:
          if not isinstance(i["database"], str):
              module.fail_json(msg="database key should be str")
          elif not isinstance(i["collection"], str):
              module.fail_json(msg="collection key should be str")
          elif i["state"] == "present" and "keys" not in i.keys():
              module.fail_json(msg="keys must be supplied when state is present")
          elif i["state"] == "present" and not isinstance(i["keys"], dict):
              module.fail_json(msg="keys key should be dict")
          elif not isinstance(i["options"], dict):
              module.fail_json(msg="options key should be dict")
          elif "name" not in i["options"]:
              module.fail_json(msg="The options dict must contain a name field")
          elif i["state"] not in ["present", "absent"]:
              module.fail_json(msg="state must be one of present or absent")
  
  
  # ================
  # Module execution
  #
  def main():
      argument_spec = mongodb_common_argument_spec()
      argument_spec.update(
          indexes=dict(type='list', elements='raw', required=True),
          replica_set=dict(type='str'),
      )
      module = AnsibleModule(
          argument_spec=argument_spec,
          supports_check_mode=True,
          required_together=[['login_user', 'login_password']],
      )
  
      if not pymongo_found:
          module.fail_json(msg=missing_required_lib('pymongo'),
                           exception=PYMONGO_IMP_ERR)
  
      validate_module(module)
  
      login_user = module.params['login_user']
      login_password = module.params['login_password']
      login_database = module.params['login_database']
      login_host = module.params['login_host']
      login_port = module.params['login_port']
      ssl = module.params['ssl']
      indexes = module.params['indexes']
      replica_set = module.params['replica_set']
  
      connection_params = {
          'host': login_host,
          'port': login_port,
      }
  
      if replica_set:
          connection_params["replicaset"] = replica_set
  
      if ssl:
          connection_params = ssl_connection_options(connection_params, module)
  
      client = MongoClient(**connection_params)
      mongo_auth(module, client)
  
      # Pre flight checks done
      indexes_created = []
      indexes_dropped = []
      changed = None
      for i in indexes:
          try:
              idx = index_exists(client, i["database"], i["collection"], i["options"]["name"])
          except Exception as excep:
              module.fail_json(msg="Could not determine index status: {0}".format(str(excep)))
          if module.check_mode:
              if idx:
                  if i["state"] == "present":
                      changed = False
                  elif i["state"] == "absent":
                      indexes_dropped.append("{0}.{1}.{2}".format(i["database"],
                                                                  i["collection"],
                                                                  i["options"]["name"]))
                      changed = True
              else:
                  if i["state"] == "present":
                      indexes_created.append("{0}.{1}.{2}".format(i["database"],
                                                                  i["collection"],
                                                                  i["options"]["name"]))
                      changed = True
                  elif i["state"] == "absent":
                      changed = False
          else:
              if idx:
                  if i["state"] == "present":
                      changed = False
                  elif i["state"] == "absent":
                      try:
                          drop_index(client, i["database"], i["collection"],
                                     i["options"]["name"])
                          indexes_dropped.append("{0}.{1}.{2}".format(i["database"],
                                                                      i["collection"],
                                                                      i["options"]["name"]))
                          changed = True
                      except Exception as excep:
                          module.fail_json(msg="Error dropping index: {0}".format(str(excep)))
  
              else:
                  if i["state"] == "present":
                      try:
                          create_index(client=client,
                                       database=i["database"],
                                       collection=i["collection"],
                                       keys=i["keys"],
                                       options=i["options"])
                          indexes_created.append("{0}.{1}.{2}".format(i["database"],
                                                                      i["collection"],
                                                                      i["options"]["name"]))
                          changed = True
                      except Exception as excep:
                          module.fail_json(msg="Error creating index: {0}".format(str(excep)))
                  elif i["state"] == "absent":
                      changed = False
  
      module.exit_json(changed=changed,
                       indexes_created=indexes_created,
                       indexes_dropped=indexes_dropped)
  
  
  if __name__ == '__main__':
      main()
  