1#!/usr/bin/python
2# Copyright: Ansible Project
3# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
4
5from __future__ import absolute_import, division, print_function
6__metaclass__ = type
7
8
9ANSIBLE_METADATA = {'metadata_version': '1.1',
10                    'status': ['stableinterface'],
11                    'supported_by': 'community'}
12
13
14DOCUMENTATION = """
15---
16module: sqs_queue
17short_description: Creates or deletes AWS SQS queues.
18description:
19  - Create or delete AWS SQS queues.
20  - Update attributes on existing queues.
21version_added: "2.0"
22author:
23  - Alan Loi (@loia)
24  - Fernando Jose Pando (@nand0p)
25  - Nadir Lloret (@nadirollo)
26requirements:
27  - "boto >= 2.33.0"
28options:
29  state:
30    description:
31      - Create or delete the queue
32    required: false
33    choices: ['present', 'absent']
34    default: 'present'
35  name:
36    description:
37      - Name of the queue.
38    required: true
39  default_visibility_timeout:
40    description:
41      - The default visibility timeout in seconds.
42  message_retention_period:
43    description:
44      - The message retention period in seconds.
45  maximum_message_size:
46    description:
47      - The maximum message size in bytes.
48  delivery_delay:
49    description:
50      - The delivery delay in seconds.
51  receive_message_wait_time:
52    description:
53      - The receive message wait time in seconds.
54  policy:
55    description:
56      - The json dict policy to attach to queue
57    version_added: "2.1"
58  redrive_policy:
59    description:
60      - json dict with the redrive_policy (see example)
61    version_added: "2.2"
62extends_documentation_fragment:
63    - aws
64    - ec2
65"""
66
67RETURN = '''
68default_visibility_timeout:
69    description: The default visibility timeout in seconds.
70    type: int
71    returned: always
72    sample: 30
73delivery_delay:
74    description: The delivery delay in seconds.
75    type: int
76    returned: always
77    sample: 0
78maximum_message_size:
79    description: The maximum message size in bytes.
80    type: int
81    returned: always
82    sample: 262144
83message_retention_period:
84    description: The message retention period in seconds.
85    type: int
86    returned: always
87    sample: 345600
88name:
89    description: Name of the SQS Queue
90    type: str
91    returned: always
92    sample: "queuename-987d2de0"
93queue_arn:
94    description: The queue's Amazon resource name (ARN).
95    type: str
96    returned: on successful creation or update of the queue
97    sample: 'arn:aws:sqs:us-east-1:199999999999:queuename-987d2de0'
98receive_message_wait_time:
99    description: The receive message wait time in seconds.
100    type: int
101    returned: always
102    sample: 0
103region:
104    description: Region that the queue was created within
105    type: str
106    returned: always
107    sample: 'us-east-1'
108'''
109
110EXAMPLES = '''
111# Create SQS queue with redrive policy
112- sqs_queue:
113    name: my-queue
114    region: ap-southeast-2
115    default_visibility_timeout: 120
116    message_retention_period: 86400
117    maximum_message_size: 1024
118    delivery_delay: 30
119    receive_message_wait_time: 20
120    policy: "{{ json_dict }}"
121    redrive_policy:
122      maxReceiveCount: 5
123      deadLetterTargetArn: arn:aws:sqs:eu-west-1:123456789012:my-dead-queue
124
125# Delete SQS queue
126- sqs_queue:
127    name: my-queue
128    region: ap-southeast-2
129    state: absent
130'''
131
132import json
133import traceback
134
135try:
136    import boto.sqs
137    from boto.exception import BotoServerError, NoAuthHandlerFound
138    HAS_BOTO = True
139
140except ImportError:
141    HAS_BOTO = False
142
143from ansible.module_utils.basic import AnsibleModule
144from ansible.module_utils.ec2 import AnsibleAWSError, connect_to_aws, ec2_argument_spec, get_aws_connection_info
145
146
147def create_or_update_sqs_queue(connection, module):
148    queue_name = module.params.get('name')
149
150    queue_attributes = dict(
151        default_visibility_timeout=module.params.get('default_visibility_timeout'),
152        message_retention_period=module.params.get('message_retention_period'),
153        maximum_message_size=module.params.get('maximum_message_size'),
154        delivery_delay=module.params.get('delivery_delay'),
155        receive_message_wait_time=module.params.get('receive_message_wait_time'),
156        policy=module.params.get('policy'),
157        redrive_policy=module.params.get('redrive_policy')
158    )
159
160    result = dict(
161        region=module.params.get('region'),
162        name=queue_name,
163    )
164    result.update(queue_attributes)
165
166    try:
167        queue = connection.get_queue(queue_name)
168        if queue:
169            # Update existing
170            result['changed'] = update_sqs_queue(queue, check_mode=module.check_mode, **queue_attributes)
171        else:
172            # Create new
173            if not module.check_mode:
174                queue = connection.create_queue(queue_name)
175                update_sqs_queue(queue, **queue_attributes)
176            result['changed'] = True
177
178        if not module.check_mode:
179            result['queue_arn'] = queue.get_attributes('QueueArn')['QueueArn']
180            result['default_visibility_timeout'] = queue.get_attributes('VisibilityTimeout')['VisibilityTimeout']
181            result['message_retention_period'] = queue.get_attributes('MessageRetentionPeriod')['MessageRetentionPeriod']
182            result['maximum_message_size'] = queue.get_attributes('MaximumMessageSize')['MaximumMessageSize']
183            result['delivery_delay'] = queue.get_attributes('DelaySeconds')['DelaySeconds']
184            result['receive_message_wait_time'] = queue.get_attributes('ReceiveMessageWaitTimeSeconds')['ReceiveMessageWaitTimeSeconds']
185
186    except BotoServerError:
187        result['msg'] = 'Failed to create/update sqs queue due to error: ' + traceback.format_exc()
188        module.fail_json(**result)
189    else:
190        module.exit_json(**result)
191
192
193def update_sqs_queue(queue,
194                     check_mode=False,
195                     default_visibility_timeout=None,
196                     message_retention_period=None,
197                     maximum_message_size=None,
198                     delivery_delay=None,
199                     receive_message_wait_time=None,
200                     policy=None,
201                     redrive_policy=None):
202    changed = False
203
204    changed = set_queue_attribute(queue, 'VisibilityTimeout', default_visibility_timeout,
205                                  check_mode=check_mode) or changed
206    changed = set_queue_attribute(queue, 'MessageRetentionPeriod', message_retention_period,
207                                  check_mode=check_mode) or changed
208    changed = set_queue_attribute(queue, 'MaximumMessageSize', maximum_message_size,
209                                  check_mode=check_mode) or changed
210    changed = set_queue_attribute(queue, 'DelaySeconds', delivery_delay,
211                                  check_mode=check_mode) or changed
212    changed = set_queue_attribute(queue, 'ReceiveMessageWaitTimeSeconds', receive_message_wait_time,
213                                  check_mode=check_mode) or changed
214    changed = set_queue_attribute(queue, 'Policy', policy,
215                                  check_mode=check_mode) or changed
216    changed = set_queue_attribute(queue, 'RedrivePolicy', redrive_policy,
217                                  check_mode=check_mode) or changed
218    return changed
219
220
221def set_queue_attribute(queue, attribute, value, check_mode=False):
222    if not value and value != 0:
223        return False
224
225    try:
226        existing_value = queue.get_attributes(attributes=attribute)[attribute]
227    except Exception:
228        existing_value = ''
229
230    # convert dict attributes to JSON strings (sort keys for comparing)
231    if attribute in ['Policy', 'RedrivePolicy']:
232        value = json.dumps(value, sort_keys=True)
233        if existing_value:
234            existing_value = json.dumps(json.loads(existing_value), sort_keys=True)
235
236    if str(value) != existing_value:
237        if not check_mode:
238            queue.set_attribute(attribute, value)
239        return True
240
241    return False
242
243
244def delete_sqs_queue(connection, module):
245    queue_name = module.params.get('name')
246
247    result = dict(
248        region=module.params.get('region'),
249        name=queue_name,
250    )
251
252    try:
253        queue = connection.get_queue(queue_name)
254        if queue:
255            if not module.check_mode:
256                connection.delete_queue(queue)
257            result['changed'] = True
258
259        else:
260            result['changed'] = False
261
262    except BotoServerError:
263        result['msg'] = 'Failed to delete sqs queue due to error: ' + traceback.format_exc()
264        module.fail_json(**result)
265    else:
266        module.exit_json(**result)
267
268
269def main():
270    argument_spec = ec2_argument_spec()
271    argument_spec.update(dict(
272        state=dict(default='present', choices=['present', 'absent']),
273        name=dict(required=True, type='str'),
274        default_visibility_timeout=dict(type='int'),
275        message_retention_period=dict(type='int'),
276        maximum_message_size=dict(type='int'),
277        delivery_delay=dict(type='int'),
278        receive_message_wait_time=dict(type='int'),
279        policy=dict(type='dict', required=False),
280        redrive_policy=dict(type='dict', required=False),
281    ))
282
283    module = AnsibleModule(
284        argument_spec=argument_spec,
285        supports_check_mode=True)
286
287    if not HAS_BOTO:
288        module.fail_json(msg='boto required for this module')
289
290    region, ec2_url, aws_connect_params = get_aws_connection_info(module)
291    if not region:
292        module.fail_json(msg='region must be specified')
293
294    try:
295        connection = connect_to_aws(boto.sqs, region, **aws_connect_params)
296
297    except (NoAuthHandlerFound, AnsibleAWSError) as e:
298        module.fail_json(msg=str(e))
299
300    state = module.params.get('state')
301    if state == 'present':
302        create_or_update_sqs_queue(connection, module)
303    elif state == 'absent':
304        delete_sqs_queue(connection, module)
305
306
307if __name__ == '__main__':
308    main()
309