1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3 4# Copyright: (c) 2015, Manuel Sousa <manuel.sousa@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 10ANSIBLE_METADATA = { 11 'metadata_version': '1.1', 12 'status': ['preview'], 13 'supported_by': 'community' 14} 15 16DOCUMENTATION = ''' 17--- 18module: rabbitmq_binding 19author: Manuel Sousa (@manuel-sousa) 20version_added: "2.0" 21 22short_description: Manage rabbitMQ bindings 23description: 24 - This module uses rabbitMQ REST APIs to create / delete bindings. 25requirements: [ "requests >= 1.0.0" ] 26options: 27 state: 28 description: 29 - Whether the bindings should be present or absent. 30 choices: [ "present", "absent" ] 31 default: present 32 name: 33 description: 34 - source exchange to create binding on. 35 required: true 36 aliases: [ "src", "source" ] 37 destination: 38 description: 39 - destination exchange or queue for the binding. 40 required: true 41 aliases: [ "dst", "dest" ] 42 destination_type: 43 description: 44 - Either queue or exchange. 45 required: true 46 choices: [ "queue", "exchange" ] 47 aliases: [ "type", "dest_type" ] 48 routing_key: 49 description: 50 - routing key for the binding. 51 default: "#" 52 arguments: 53 description: 54 - extra arguments for exchange. If defined this argument is a key/value dictionary 55 required: false 56 default: {} 57extends_documentation_fragment: 58 - rabbitmq 59''' 60 61EXAMPLES = ''' 62# Bind myQueue to directExchange with routing key info 63- rabbitmq_binding: 64 name: directExchange 65 destination: myQueue 66 type: queue 67 routing_key: info 68 69# Bind directExchange to topicExchange with routing key *.info 70- rabbitmq_binding: 71 name: topicExchange 72 destination: topicExchange 73 type: exchange 74 routing_key: '*.info' 75''' 76 77import json 78import traceback 79 80REQUESTS_IMP_ERR = None 81try: 82 import requests 83 HAS_REQUESTS = True 84except ImportError: 85 REQUESTS_IMP_ERR = traceback.format_exc() 86 HAS_REQUESTS = False 87 88from ansible.module_utils.six.moves.urllib import parse as urllib_parse 89from ansible.module_utils.basic import AnsibleModule, missing_required_lib 90from ansible.module_utils.rabbitmq import rabbitmq_argument_spec 91 92 93class RabbitMqBinding(object): 94 def __init__(self, module): 95 """ 96 :param module: 97 """ 98 self.module = module 99 self.name = self.module.params['name'] 100 self.login_user = self.module.params['login_user'] 101 self.login_password = self.module.params['login_password'] 102 self.login_host = self.module.params['login_host'] 103 self.login_port = self.module.params['login_port'] 104 self.login_protocol = self.module.params['login_protocol'] 105 self.vhost = self.module.params['vhost'] 106 self.destination = self.module.params['destination'] 107 self.destination_type = 'q' if self.module.params['destination_type'] == 'queue' else 'e' 108 self.routing_key = self.module.params['routing_key'] 109 self.arguments = self.module.params['arguments'] 110 self.verify = self.module.params['ca_cert'] 111 self.cert = self.module.params['client_cert'] 112 self.key = self.module.params['client_key'] 113 self.props = urllib_parse.quote(self.routing_key) if self.routing_key != '' else '~' 114 self.base_url = '{0}://{1}:{2}/api/bindings'.format(self.login_protocol, 115 self.login_host, 116 self.login_port) 117 self.url = '{0}/{1}/e/{2}/{3}/{4}/{5}'.format(self.base_url, 118 urllib_parse.quote(self.vhost, safe=''), 119 urllib_parse.quote(self.name, safe=''), 120 self.destination_type, 121 urllib_parse.quote(self.destination, safe=''), 122 self.props) 123 self.result = { 124 'changed': False, 125 'name': self.module.params['name'], 126 } 127 self.authentication = ( 128 self.login_user, 129 self.login_password 130 ) 131 self.request = requests 132 self.http_check_states = { 133 200: True, 134 404: False, 135 } 136 self.http_actionable_states = { 137 201: True, 138 204: True, 139 } 140 self.api_result = self.request.get(self.url, auth=self.authentication, verify=self.verify, cert=(self.cert, self.key)) 141 142 def run(self): 143 """ 144 :return: 145 """ 146 self.check_presence() 147 self.check_mode() 148 self.action_mode() 149 150 def check_presence(self): 151 """ 152 :return: 153 """ 154 if self.check_should_throw_fail(): 155 self.fail() 156 157 def change_required(self): 158 """ 159 :return: 160 """ 161 if self.module.params['state'] == 'present': 162 if not self.is_present(): 163 return True 164 elif self.module.params['state'] == 'absent': 165 if self.is_present(): 166 return True 167 return False 168 169 def is_present(self): 170 """ 171 :return: 172 """ 173 return self.http_check_states.get(self.api_result.status_code, False) 174 175 def check_mode(self): 176 """ 177 :return: 178 """ 179 if self.module.check_mode: 180 result = self.result 181 result['changed'] = self.change_required() 182 result['details'] = self.api_result.json() if self.is_present() else self.api_result.text 183 result['arguments'] = self.module.params['arguments'] 184 self.module.exit_json(**result) 185 186 def check_reply_is_correct(self): 187 """ 188 :return: 189 """ 190 if self.api_result.status_code in self.http_check_states: 191 return True 192 return False 193 194 def check_should_throw_fail(self): 195 """ 196 :return: 197 """ 198 if not self.is_present(): 199 if not self.check_reply_is_correct(): 200 return True 201 return False 202 203 def action_mode(self): 204 """ 205 :return: 206 """ 207 result = self.result 208 if self.change_required(): 209 if self.module.params['state'] == 'present': 210 self.create() 211 if self.module.params['state'] == 'absent': 212 self.remove() 213 if self.action_should_throw_fail(): 214 self.fail() 215 result['changed'] = True 216 result['destination'] = self.module.params['destination'] 217 self.module.exit_json(**result) 218 else: 219 result['changed'] = False 220 self.module.exit_json(**result) 221 222 def action_reply_is_correct(self): 223 """ 224 :return: 225 """ 226 if self.api_result.status_code in self.http_actionable_states: 227 return True 228 return False 229 230 def action_should_throw_fail(self): 231 """ 232 :return: 233 """ 234 if not self.action_reply_is_correct(): 235 return True 236 return False 237 238 def create(self): 239 """ 240 :return: 241 """ 242 self.url = '{0}/{1}/e/{2}/{3}/{4}'.format(self.base_url, 243 urllib_parse.quote(self.vhost, safe=''), 244 urllib_parse.quote(self.name, safe=''), 245 self.destination_type, 246 urllib_parse.quote(self.destination, safe='')) 247 self.api_result = self.request.post(self.url, 248 auth=self.authentication, 249 verify=self.verify, 250 cert=(self.cert, self.key), 251 headers={"content-type": "application/json"}, 252 data=json.dumps({ 253 'routing_key': self.routing_key, 254 'arguments': self.arguments 255 })) 256 257 def remove(self): 258 """ 259 :return: 260 """ 261 self.api_result = self.request.delete(self.url, auth=self.authentication, verify=self.verify, cert=(self.cert, self.key)) 262 263 def fail(self): 264 """ 265 :return: 266 """ 267 self.module.fail_json( 268 msg="Unexpected reply from API", 269 status=self.api_result.status_code, 270 details=self.api_result.text 271 ) 272 273 274def main(): 275 276 argument_spec = rabbitmq_argument_spec() 277 argument_spec.update( 278 dict( 279 state=dict(default='present', choices=['present', 'absent'], type='str'), 280 name=dict(required=True, aliases=["src", "source"], type='str'), 281 destination=dict(required=True, aliases=["dst", "dest"], type='str'), 282 destination_type=dict(required=True, aliases=["type", "dest_type"], choices=["queue", "exchange"], 283 type='str'), 284 routing_key=dict(default='#', type='str'), 285 arguments=dict(default=dict(), type='dict') 286 ) 287 ) 288 module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) 289 290 if not HAS_REQUESTS: 291 module.fail_json(msg=missing_required_lib("requests"), exception=REQUESTS_IMP_ERR) 292 293 RabbitMqBinding(module).run() 294 295 296if __name__ == '__main__': 297 main() 298