
    VhV                         d dl mZmZmZ eZdZdZdZd dl	m
Z
 d dlmZ d dlmZmZmZmZmZmZmZmZ d Zd	 Zd
 Zd Zd Zd Zd Zd Zd Zedk(  r e        yy)    )absolute_importdivisionprint_functiona  
---
module: mongodb_replicaset
short_description: Initialises a MongoDB replicaset.
description:
  - Initialises a MongoDB replicaset in a new deployment.
  - Validates the replicaset name for existing deployments.
  - Advanced replicaset member (re)configuration possible (see examples).
  - Initialize the replicaset before adding users as per \
    [best practice](https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set-with-keyfile-access-control/).
author: Rhys Campbell (@rhysmeister)
version_added: "1.0.0"

extends_documentation_fragment:
  - community.mongodb.login_options
  - community.mongodb.ssl_options

options:
  replica_set:
    description:
    - Replicaset name.
    type: str
    default: rs0
  members:
    description:
    - Yaml list consisting of the replicaset members.
    - Csv string will also be accepted i.e. mongodb1:27017,mongodb2:27017,mongodb3:27017.
    - A dictionary can also be used to specify advanced replicaset member options.
    - If a port number is not provided then 27017 is assumed.
    type: list
    elements: raw
  validate:
    description:
    - Performs some basic validation on the provided replicaset config.
    type: bool
    default: yes
  arbiter_at_index:
    description:
    - Identifies the position of the member in the array that is an arbiter.
    type: int
  chaining_allowed:
    description:
    - When I(settings.chaining_allowed=true), the replicaset allows secondary members to replicate from other
      secondary members.
    - When I(settings.chaining_allowed=false), secondaries can replicate only from the primary.
    type: bool
    default: yes
  heartbeat_timeout_secs:
    description:
    - Number of seconds that the replicaset members wait for a successful heartbeat from each other.
    - If a member does not respond in time, other members mark the delinquent member as inaccessible.
    - The setting only applies when using I(protocol_version=0). When using I(protocol_version=1) the relevant
      setting is I(settings.election_timeout_millis).
    type: int
    default: 10
  election_timeout_millis:
    description:
    - The time limit in milliseconds for detecting when a replicaset's primary is unreachable.
    type: int
    default: 10000
  protocol_version:
    description: Version of the replicaset election protocol.
    type: int
    choices: [ 0, 1 ]
    default: 1
  reconfigure:
    description:
      - This feature is currently experimental. Please test your scenario thoroughly.
      - Consult the integration test file for supported scenarios - \
        [Integration tests](https://github.com/ansible-collections/community.mongodb/tree/master/tests/integration/targets/mongodb_replicaset/tasks). \
        See files prefixed with 330.
      - Whether to perform replicaset reconfiguration actions.
      - Only relevant when the replicaset already exists.
      - Only one member should be removed or added per invocation.
      - Members should be specific as either all strings or all dicts when reconfiguring.
      - Currently no support for replicaset settings document changes.
      - Please always specify ports in the replicaset list when using this feature or some operations may fail.
    type: bool
    default: false
  force:
    description:
      - Only relevant when reconfigure = true.
      - Specify true to force the available replica set members to accept the new configuration.
      - Force reconfiguration can result in unexpected or undesired behavior, including rollback of "majority" committed writes.
    type: bool
    default: false
  max_time_ms:
    description:
      - Specifies a cumulative time limit in milliseconds for processing the replicaset reconfiguration.
    type: int
    default: null
  debug:
    description:
      - Add additonal info for debug.
    type: bool
    default: false
  cluster_cmd:
    description:
      - Command the module should use to obtain information about the MongoDB node we are connecting to.
    type: str
    choices:
      - isMaster
      - hello
    default: hello
notes:
- Requires the pymongo Python package on the remote host, version 4+.. This
  can be installed using pip or the OS package manager.
  @see U(http://api.mongodb.org/python/current/installation.html)
requirements:
- pymongo
a  
# Create a replicaset called 'rs0' with the 3 provided members
- name: Ensure replicaset rs0 exists
  community.mongodb.mongodb_replicaset:
    login_host: localhost
    login_user: admin
    login_password: admin
    replica_set: rs0
    members:
    - mongodb1:27017
    - mongodb2:27017
    - mongodb3:27017
  when: groups.mongod.index(inventory_hostname) == 0

# Create two single-node replicasets on the localhost for testing
- name: Ensure replicaset rs0 exists
  community.mongodb.mongodb_replicaset:
    login_host: localhost
    login_port: 3001
    login_user: admin
    login_password: secret
    login_database: admin
    replica_set: rs0
    members: localhost:3001
    validate: no

- name: Ensure replicaset rs1 exists
  community.mongodb.mongodb_replicaset:
    login_host: localhost
    login_port: 3002
    login_user: admin
    login_password: secret
    login_database: admin
    replica_set: rs1
    members: localhost:3002
    validate: no

- name: Create a replicaset and use a custom priority for each member
  community.mongodb.mongodb_replicaset:
    login_host: localhost
    login_user: admin
    login_password: admin
    replica_set: rs0
    members:
    - host: "localhost:3001"
      priority: 1
    - host: "localhost:3002"
      priority: 0.5
    - host: "localhost:3003"
      priority: 0.5
  when: groups.mongod.index(inventory_hostname) == 0

- name: Create replicaset rs1 with options and member tags
  community.mongodb.mongodb_replicaset:
    login_host: localhost
    login_port: 3001
    login_database: admin
    replica_set: rs1
    members:
    - host: "localhost:3001"
      priority: 1
      tags:
        dc: "east"
        usage: "production"
    - host: "localhost:3002"
      priority: 1
      tags:
        dc: "east"
        usage: "production"
    - host: "localhost:3003"
      priority: 0
      hidden: true
      slaveDelay: 3600
      tags:
        dc: "west"
        usage: "reporting"

- name: Replicaset with one arbiter node (mongodb3 - index is zero-based)
  community.mongodb.mongodb_replicaset:
    login_user: admin
    login_password: admin
    replica_set: rs0
    members:
      - mongodb1:27017
      - mongodb2:27017
      - mongodb3:27017
    arbiter_at_index: 2
  when: groups.mongod.index(inventory_hostname) == 0

- name: Add a new member to a replicaset - Safe for pre-5.0 consult documentation - https://docs.mongodb.com/manual/tutorial/expand-replica-set/
  block:
    - name: Create replicaset with module - with dicts
      community.mongodb.mongodb_replicaset:
        replica_set: "rs0"
        members:
           - host: localhost:3001
           - host: localhost:3002
           - host: localhost:3003

    - name: Wait for the replicaset to stabilise
      community.mongodb.mongodb_status:
        replica_set: "rs0"
        poll: 5
        interval: 10

    - name: Remove a member from the replicaset
      community.mongodb.mongodb_replicaset:
        replica_set: "rs0"
        reconfigure: yes
        members:
           - host: localhost:3001
           - host: localhost:3002

    - name: Wait for the replicaset to stabilise after member removal
      community.mongodb.mongodb_status:
        replica_set: "rs0"
        validate: minimal
        poll: 5
        interval: 10

    - name: Add a member to the replicaset
      community.mongodb.mongodb_replicaset:
        replica_set: "rs0"
        reconfigure: yes
        members:
           - host: localhost:3001
           - host: localhost:3002
           - host: localhost:3004
             hidden: true
             votes: 0
             priority: 0

    - name: Wait for the replicaset to stabilise after member addition
      community.mongodb.mongodb_status:
        replica_set: "rs0"
        validate: minimal
        poll: 5
        interval: 30

    - name: Reconfigure the replicaset - Make member 3004 a normal voting member
      community.mongodb.mongodb_replicaset:
        replica_set: "rs0"
        reconfigure: yes
        members:
           - host: localhost:3001
           - host: localhost:3002
           - host: localhost:3004
             hidden: false
             votes: 1
             priority: 1

    - name: Wait for the replicaset to stabilise
      community.mongodb.mongodb_status:
        replica_set: "rs0"
        poll: 5
        interval: 30
z
mongodb_replicaset:
  description: The name of the replicaset that has been created.
  returned: success
  type: str
reconfigure:
  description: If a replicaset reconfiguration occured.
  returned: On rpelicaset reconfiguration
  type: bool
)AnsibleModule)	to_native)missing_required_libmongodb_common_argument_spec
mongo_authmember_dicts_differentlists_are_differentPYMONGO_IMP_ERRpymongo_foundget_mongodb_clientc                 F    | j                   j                  ddi      }|d   S )NreplSetGetConfig   config)admincommand)clientconfs     x/home/dcms/DCMS/lib/python3.12/site-packages/ansible_collections/community/mongodb/plugins/modules/mongodb_replicaset.pyget_replicaset_configr   1  s&    <<!3Q 78D>    c                 \    t        |       }g }|d   D ]  }|j                  |d           |S )Nmembershost)r   append)r   r   r   members       r   get_member_namesr    6  s9     (DGy/ 'vf~&'Nr   c                    	 ddl m} g }g }d}t        d |D              r|d   D ]=  }|d   |v s|j                  |       |j                  |d          |d	   |kD  s9|d	   }? t        t        |      t        |      z
        }	t        |	      dkD  r5|	D ]0  }
d
|
vr|
dz  }
|j                   d	|dz   fd|
fg             |dz  }2 ||d<   |S t        d |D              rg }i }g }d}|d   D ]  }
|
d	   ||
d   <   |
d	   |kD  s|
d	   } |D ]:  }
|
d   |v s||
d      |
d	<   |j                  |
d          |j                  |
       < |D ]%  }
|
d   |vs|dz   }||
d	<   |j                  |
       ' ||d<   |S | j	                  d       |S # t        $ rG}	 ddlm} n4# t        $ r(}| j	                  dt        |      z         Y d}~nd}~ww xY wY d}~d}~ww xY w)z
    Modifies the members section of the config document as appropriate.
    @module - Ansible module object
    @config - Replicaset config document from MongoDB
    @members - Members config from module
    r   OrderedDict[Cannot import OrderedDict class. You can probably install with: pip install ordereddict: %smsgNc              3   <   K   | ]  }t        |t                y wN)
isinstancestr.0r   s     r   	<genexpr>z!modify_members.<locals>.<genexpr>P  s     
9v:fc"
9   r   r   _id::27017r   c              3   <   K   | ]  }t        |t                y wr(   )r)   dictr+   s     r   r-   z!modify_members.<locals>.<genexpr>_  s     <&Z%<r.   z7All items in members must be either of type dict of str)collectionsr#   ImportErrorordereddict	fail_jsonr   allr   listsetlen)moduler   r   r#   excepnew_member_configexisting_membersmax_idcurrent_membermember_additionsr   matched_memberss               r   modify_membersrD   >  s   1+ F

9
99$Y/ 	3Nf%0!((8 ''v(>?!%(61+E2F	3  Gs3C/D DE 1$* f$h&F!((ufqj6IFTZK[5\)]^!	
 .y< M; 
<G<	< Y' 	'F/5e}VF^,e}v%	'
  	1Ff~!11 0 @u&&vf~6!((0		1
  	1Ff~_4! &u!((0		1
 .y M 	VWMk  1	1/ 	1!~(/"0 1 1	11s;   F 	G F! G!	G*GGGGG c                 L   |dxx   dz  cc<   	 ddl m}  d|fd|fg      }||j                  d	|i       |j                  j                  |       y # t        $ rF}	 ddlm} n4# t        $ r(}| j	                  dt        |      z         Y d }~nd }~ww xY wY d }~d }~ww xY w)
Nversionr   r   r"   r$   r%   replSetReconfigforce	maxTimeMS)	r4   r#   r5   r6   r7   r   updater   r   )r<   r   r   rH   max_time_msr#   r=   cmd_docs           r   replicaset_reconfigurerM     s    
91+ -v6#U+- .G[12
LL!  1	1/ 	1!~(/"0 1 1	11s;   A 	B#A%$B%	B.BBBBB#c                 P    | d   j                  |      }d|v rt        |d         S y)zCheck if a replicaset exists.

    Args:
        client (cursor): Mongodb cursor on admin database.
        cluster_cmd (str): Either isMaster or hello

    Returns:
        str: when the node is a member of a replicaset , False otherwise.
    r   setNameF)r   r*   )r   cluster_cmddocs      r   replicaset_findrR     s2     /
!
!+
.CC3y>""r   c	           
         	 ddl m}	 g }d}dt        |      i}|dk(  r||d<   n||d<   |D ]  }t        |t              rBd|vr|d	z  }|j                   	d
t        |      fd|fg             ||k(  rd||   d<   |dz  }Vt        |t              rv|d   }d|vr|d	z  }|j                   	d
t        |      fd|fg             t        |j                               D ]  }|dk7  s	||   ||   |<    ||k(  rd||   d<   |dz  }t        dj                  t        t!        |                          	d
|fd|fd|fd|fg      }	 |d   j#                  d|       y # t        $ rG}
	 ddlm}	 n4# t        $ r(}
| j	                  dt        |
      z         Y d }
~
nd }
~
ww xY wY d }
~
d }
~
ww xY w# t$        $ r2}
t%        dj                  t        |
      t        |                  d }
~
ww xY w)Nr   r"   r$   r%   chainingAllowedheartbeatTimeoutSecselectionTimeoutMillisr0   r1   r/   r   TarbiterOnlyr   z2member should be a str or dict. Instead found: {0}protocolVersionr   settingsr   replSetInitiatezSome problem {0} | {1})r4   r#   r5   r6   r7   r   boolr)   r*   r   intr3   r9   keys
ValueErrorformattyper   	Exception)r<   r   replica_setr   arbiter_at_indexprotocol_versionchaining_allowedheartbeat_timeout_secselection_timeout_millisr#   r=   members_dict_listindexrY   r   hostnamekeyr   s                     r   replicaset_addrl     s]   1+ E4 01H 1+A'(,C() nfc"& ("$$[5#e*2EPVGW1X%YZ((:>!%(7QJE%f~H("H$$$[5#e*2EPXGY1Z%[\FKKM* @&=4:3K%e,S1@ ((:>!%(7QJEQXXY\]abi]jYklmm)n, ,*,<="$56#X.0 1DPw 148W  1	1/ 	1!~(/"0 1 1	11X  P077E
CINOOPsS   E 	F2 	F/)E0/F*0	F!9FF*F!!F**F/2	G-;-G((G-c                     t         r(   )NotImplementedError)r<   r   rb   s      r   replicaset_removero     s    
r   c                    | j                   d   }| j                   d   }| j                   d   }d}d }d }		 t        |      }	t        |d   t              rt        |t        |            }n2t        |d   t              rt        |	|      }n| j                  d       |rS| j                  s;	 t        | |	|      }|rt        |	      |d	<   t        |      |d
<   t        | ||||       d|d<   d|d<   |S d|d<   |S # t        $ r+}
| j                  dj	                  |
             Y d }
~
d }
~
ww xY w# t        $ r,}
| j                  dj	                  |
|             Y d }
~
zd }
~
ww xY w)NdebugrH   rK   Fz*Unable to get replicaset configuration {0}r%   r   z"members must be either str or dictr   modified_configz3Failed reconfiguring replicaset {0}, config doc {1}Tchangedzreplicaset reconfiguredr&   )paramsr   ra   r7   r_   r)   r*   r   r    r3   r   
check_moderD   rM   )r<   r   r   resultrq   rH   rK   diffrr   r   r=   s              r   modify_members_flowrx     s   MM'"EMM'"E--.KDOFY&v. '!*c""7,<V,DE	GAJ	%%fg6AB  {"0"I'*6{F8$03O0DF,-&vv{[ !y1u M "yM/  YIPPQVWXXY"  {  %Z%a%abgix%y zz{s/   C7 *:D. 7	D+ !D&&D+.	E#7"EE#c                     t               } | j                  t        d      t        dd      t        dd      t        dd      t        dd	
      t        ddddg      t        dd      t        dd      t        dd      t        dd      t        dd       t        dd      t        dddgd             t        | dddgg      }t        s |j                  t        d      t               |j                  d   }|j                  d   }|j                  d   }|j                  d   }|j                  d   }|j                  d   }|j                  d    }|j                  d!   }	|j                  d"   }
|j                  d#   }|j                  d$   }|j                  d%   }|j                  d&   }t        |D cg c]+  }t        |t              r|j                  d'd      dk(  rdnd- c}      }|rQ|
du rMt        |      d(k  s|d(z  dk(  r|j                  d)*       |#t        |      dz
  |k  r|j                  d+*       t        d|,      }	 t        |d-      }	 t!        |      }t        t"              rb||k(  r:|
rt%        |      }t'        ||||      }nd|d0<   ||d<    |j(                  d4i | y |j                  d1j+                  ||      *       y t        |      dk(  r|j                  d2*       |j,                  du r	 t/        ||||||||		       d|d0<   nd|d0<    |j(                  d4i | y c c}w # t        $ r)}|j                  d.t        |      z  *       Y d }~d }~ww xY w# t        $ r)}|j                  d/t        |      z  *       Y d }~9d }~ww xY w# t        $ r(}|j                  d3t        |      z  *       Y d }~d }~ww xY w)5Nr\   )r`   r[   T)r`   defaulti'  
   r9   raw)r`   elementsr   r   )r`   rz   choicesr*   rs0FisMasterhello)r`   r~   rz   )rc   re   rg   rf   r   rd   rb   validatereconfigurerH   rK   rq   rP   
login_userlogin_password)argument_specsupports_check_moderequired_togetherpymongo)r&   	exceptionrb   r   rc   r   rd   re   rf   rg   r   rH   rK   rq   rP   votes   zKMongoDB Replicaset validation failed. Invalid number of replicaset members.r%   z<MongoDB Replicaset validation failed. Invalid arbiter index.)rs   rb   )directConnectionz!Unable to connect to database: %sz)Unable to connect to query replicaset: %srs   z<The replica_set name of {0} does not match the expected: {1}z1Parameter replica_set must not be an empty stringz Unable to create replica_set: %s )r	   rJ   r3   r   r   r7   r   r   rt   sumr)   getr;   r   ra   r   rR   r*   r
   rx   	exit_jsonr_   ru   rl   )r   r<   rb   r   rc   r   rd   re   rf   rg   r   rH   rK   rq   rP   mvoting_membersrv   r   erss                        r   mainr     s   02M5)648 $% ?#;&515!aVDeU3640fe4.eT2.ej'-BGT   # (*:;<F 1)<#2 	 	4 --.KmmI&G}}%78}}Z(H}}%78}}%78#]]+CD$mm,EF--.KMM'"E--.KMM'"E--.K ahi\]:a#6!%%:Kq:P!VWWijNK5(w<1 2a 7!no'CL1,<?O,O!_`F
Q#FTBYV[1 "c"#FF3,VVWfM$)y!$&F=!F&v&!_!f!fgikv!wx {q !TU%Xvv{G/1A/1G68 %)y! !%F9"6"i j  Q@9Q<OPPQ
  YH9UV<WXXY6  X  %G)TU,%V WWXsN    0MM	 &M> N3 		M;M66M;>	N0N++N03	O$<OO$__main__N) 
__future__r   r   r   r`   __metaclass__DOCUMENTATIONEXAMPLESRETURNansible.module_utils.basicr   ansible.module_utils._textr   Iansible_collections.community.mongodb.plugins.module_utils.mongodb_commonr   r	   r
   r   r   r   r   r   r   r    rD   rM   rR   rl   ro   rx   r   __name__r   r   r   <module>r      s    A @n`\|	
 5 0	 	 	
>B", 2Pj!P^#B zF r   