1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# Copyright: (c) 2015, Steve Gargan <steve.gargan@gmail.com> 5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import absolute_import, division, print_function 8__metaclass__ = type 9 10DOCUMENTATION = ''' 11module: consul_session 12short_description: Manipulate consul sessions 13description: 14 - Allows the addition, modification and deletion of sessions in a consul 15 cluster. These sessions can then be used in conjunction with key value pairs 16 to implement distributed locks. In depth documentation for working with 17 sessions can be found at http://www.consul.io/docs/internals/sessions.html 18requirements: 19 - python-consul 20 - requests 21author: 22- Steve Gargan (@sgargan) 23options: 24 id: 25 description: 26 - ID of the session, required when I(state) is either C(info) or 27 C(remove). 28 type: str 29 state: 30 description: 31 - Whether the session should be present i.e. created if it doesn't 32 exist, or absent, removed if present. If created, the I(id) for the 33 session is returned in the output. If C(absent), I(id) is 34 required to remove the session. Info for a single session, all the 35 sessions for a node or all available sessions can be retrieved by 36 specifying C(info), C(node) or C(list) for the I(state); for C(node) 37 or C(info), the node I(name) or session I(id) is required as parameter. 38 choices: [ absent, info, list, node, present ] 39 type: str 40 default: present 41 name: 42 description: 43 - The name that should be associated with the session. Required when 44 I(state=node) is used. 45 type: str 46 delay: 47 description: 48 - The optional lock delay that can be attached to the session when it 49 is created. Locks for invalidated sessions ar blocked from being 50 acquired until this delay has expired. Durations are in seconds. 51 type: int 52 default: 15 53 node: 54 description: 55 - The name of the node that with which the session will be associated. 56 by default this is the name of the agent. 57 type: str 58 datacenter: 59 description: 60 - The name of the datacenter in which the session exists or should be 61 created. 62 type: str 63 checks: 64 description: 65 - Checks that will be used to verify the session health. If 66 all the checks fail, the session will be invalidated and any locks 67 associated with the session will be release and can be acquired once 68 the associated lock delay has expired. 69 type: list 70 elements: str 71 host: 72 description: 73 - The host of the consul agent defaults to localhost. 74 type: str 75 default: localhost 76 port: 77 description: 78 - The port on which the consul agent is running. 79 type: int 80 default: 8500 81 scheme: 82 description: 83 - The protocol scheme on which the consul agent is running. 84 type: str 85 default: http 86 validate_certs: 87 description: 88 - Whether to verify the TLS certificate of the consul agent. 89 type: bool 90 default: True 91 behavior: 92 description: 93 - The optional behavior that can be attached to the session when it 94 is created. This controls the behavior when a session is invalidated. 95 choices: [ delete, release ] 96 type: str 97 default: release 98''' 99 100EXAMPLES = ''' 101- name: Register basic session with consul 102 community.general.consul_session: 103 name: session1 104 105- name: Register a session with an existing check 106 community.general.consul_session: 107 name: session_with_check 108 checks: 109 - existing_check_name 110 111- name: Register a session with lock_delay 112 community.general.consul_session: 113 name: session_with_delay 114 delay: 20s 115 116- name: Retrieve info about session by id 117 community.general.consul_session: 118 id: session_id 119 state: info 120 121- name: Retrieve active sessions 122 community.general.consul_session: 123 state: list 124''' 125 126try: 127 import consul 128 from requests.exceptions import ConnectionError 129 python_consul_installed = True 130except ImportError: 131 python_consul_installed = False 132 133from ansible.module_utils.basic import AnsibleModule 134 135 136def execute(module): 137 138 state = module.params.get('state') 139 140 if state in ['info', 'list', 'node']: 141 lookup_sessions(module) 142 elif state == 'present': 143 update_session(module) 144 else: 145 remove_session(module) 146 147 148def lookup_sessions(module): 149 150 datacenter = module.params.get('datacenter') 151 152 state = module.params.get('state') 153 consul_client = get_consul_api(module) 154 try: 155 if state == 'list': 156 sessions_list = consul_client.session.list(dc=datacenter) 157 # Ditch the index, this can be grabbed from the results 158 if sessions_list and len(sessions_list) >= 2: 159 sessions_list = sessions_list[1] 160 module.exit_json(changed=True, 161 sessions=sessions_list) 162 elif state == 'node': 163 node = module.params.get('node') 164 sessions = consul_client.session.node(node, dc=datacenter) 165 module.exit_json(changed=True, 166 node=node, 167 sessions=sessions) 168 elif state == 'info': 169 session_id = module.params.get('id') 170 171 session_by_id = consul_client.session.info(session_id, dc=datacenter) 172 module.exit_json(changed=True, 173 session_id=session_id, 174 sessions=session_by_id) 175 176 except Exception as e: 177 module.fail_json(msg="Could not retrieve session info %s" % e) 178 179 180def update_session(module): 181 182 name = module.params.get('name') 183 delay = module.params.get('delay') 184 checks = module.params.get('checks') 185 datacenter = module.params.get('datacenter') 186 node = module.params.get('node') 187 behavior = module.params.get('behavior') 188 189 consul_client = get_consul_api(module) 190 191 try: 192 session = consul_client.session.create( 193 name=name, 194 behavior=behavior, 195 node=node, 196 lock_delay=delay, 197 dc=datacenter, 198 checks=checks 199 ) 200 module.exit_json(changed=True, 201 session_id=session, 202 name=name, 203 behavior=behavior, 204 delay=delay, 205 checks=checks, 206 node=node) 207 except Exception as e: 208 module.fail_json(msg="Could not create/update session %s" % e) 209 210 211def remove_session(module): 212 session_id = module.params.get('id') 213 214 consul_client = get_consul_api(module) 215 216 try: 217 consul_client.session.destroy(session_id) 218 219 module.exit_json(changed=True, 220 session_id=session_id) 221 except Exception as e: 222 module.fail_json(msg="Could not remove session with id '%s' %s" % ( 223 session_id, e)) 224 225 226def get_consul_api(module): 227 return consul.Consul(host=module.params.get('host'), 228 port=module.params.get('port'), 229 scheme=module.params.get('scheme'), 230 verify=module.params.get('validate_certs')) 231 232 233def test_dependencies(module): 234 if not python_consul_installed: 235 module.fail_json(msg="python-consul required for this module. " 236 "see https://python-consul.readthedocs.io/en/latest/#installation") 237 238 239def main(): 240 argument_spec = dict( 241 checks=dict(type='list', elements='str'), 242 delay=dict(type='int', default='15'), 243 behavior=dict(type='str', default='release', choices=['release', 'delete']), 244 host=dict(type='str', default='localhost'), 245 port=dict(type='int', default=8500), 246 scheme=dict(type='str', default='http'), 247 validate_certs=dict(type='bool', default=True), 248 id=dict(type='str'), 249 name=dict(type='str'), 250 node=dict(type='str'), 251 state=dict(type='str', default='present', choices=['absent', 'info', 'list', 'node', 'present']), 252 datacenter=dict(type='str'), 253 ) 254 255 module = AnsibleModule( 256 argument_spec=argument_spec, 257 required_if=[ 258 ('state', 'node', ['name']), 259 ('state', 'info', ['id']), 260 ('state', 'remove', ['id']), 261 ], 262 supports_check_mode=False 263 ) 264 265 test_dependencies(module) 266 267 try: 268 execute(module) 269 except ConnectionError as e: 270 module.fail_json(msg='Could not connect to consul agent at %s:%s, error was %s' % ( 271 module.params.get('host'), module.params.get('port'), e)) 272 except Exception as e: 273 module.fail_json(msg=str(e)) 274 275 276if __name__ == '__main__': 277 main() 278