mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-06 12:07:14 +00:00
Initial commit
This commit is contained in:
commit
aebc1b03fd
4861 changed files with 812621 additions and 0 deletions
181
plugins/modules/notification/bearychat.py
Normal file
181
plugins/modules/notification/bearychat.py
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2016, Jiangge Zhang <tonyseek@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['stableinterface'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: bearychat
|
||||
short_description: Send BearyChat notifications
|
||||
description:
|
||||
- The M(bearychat) module sends notifications to U(https://bearychat.com)
|
||||
via the Incoming Robot integration.
|
||||
author: "Jiangge Zhang (@tonyseek)"
|
||||
options:
|
||||
url:
|
||||
description:
|
||||
- BearyChat WebHook URL. This authenticates you to the bearychat
|
||||
service. It looks like
|
||||
C(https://hook.bearychat.com/=ae2CF/incoming/e61bd5c57b164e04b11ac02e66f47f60).
|
||||
required: true
|
||||
text:
|
||||
description:
|
||||
- Message to send.
|
||||
markdown:
|
||||
description:
|
||||
- If C(yes), text will be parsed as markdown.
|
||||
default: 'yes'
|
||||
type: bool
|
||||
channel:
|
||||
description:
|
||||
- Channel to send the message to. If absent, the message goes to the
|
||||
default channel selected by the I(url).
|
||||
attachments:
|
||||
description:
|
||||
- Define a list of attachments. For more information, see
|
||||
https://github.com/bearyinnovative/bearychat-tutorial/blob/master/robots/incoming.md#attachments
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Send notification message via BearyChat
|
||||
local_action:
|
||||
module: bearychat
|
||||
url: |
|
||||
https://hook.bearychat.com/=ae2CF/incoming/e61bd5c57b164e04b11ac02e66f47f60
|
||||
text: "{{ inventory_hostname }} completed"
|
||||
|
||||
- name: Send notification message via BearyChat all options
|
||||
local_action:
|
||||
module: bearychat
|
||||
url: |
|
||||
https://hook.bearychat.com/=ae2CF/incoming/e61bd5c57b164e04b11ac02e66f47f60
|
||||
text: "{{ inventory_hostname }} completed"
|
||||
markdown: no
|
||||
channel: "#ansible"
|
||||
attachments:
|
||||
- title: "Ansible on {{ inventory_hostname }}"
|
||||
text: "May the Force be with you."
|
||||
color: "#ffffff"
|
||||
images:
|
||||
- http://example.com/index.png
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
msg:
|
||||
description: execution result
|
||||
returned: success
|
||||
type: str
|
||||
sample: "OK"
|
||||
"""
|
||||
|
||||
try:
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse, urlunparse
|
||||
HAS_URLPARSE = True
|
||||
except Exception:
|
||||
HAS_URLPARSE = False
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
def build_payload_for_bearychat(module, text, markdown, channel, attachments):
|
||||
payload = {}
|
||||
if text is not None:
|
||||
payload['text'] = text
|
||||
if markdown is not None:
|
||||
payload['markdown'] = markdown
|
||||
if channel is not None:
|
||||
payload['channel'] = channel
|
||||
if attachments is not None:
|
||||
payload.setdefault('attachments', []).extend(
|
||||
build_payload_for_bearychat_attachment(
|
||||
module, item.get('title'), item.get('text'), item.get('color'),
|
||||
item.get('images'))
|
||||
for item in attachments)
|
||||
payload = 'payload=%s' % module.jsonify(payload)
|
||||
return payload
|
||||
|
||||
|
||||
def build_payload_for_bearychat_attachment(module, title, text, color, images):
|
||||
attachment = {}
|
||||
if title is not None:
|
||||
attachment['title'] = title
|
||||
if text is not None:
|
||||
attachment['text'] = text
|
||||
if color is not None:
|
||||
attachment['color'] = color
|
||||
if images is not None:
|
||||
target_images = attachment.setdefault('images', [])
|
||||
if not isinstance(images, (list, tuple)):
|
||||
images = [images]
|
||||
for image in images:
|
||||
if isinstance(image, dict) and 'url' in image:
|
||||
image = {'url': image['url']}
|
||||
elif hasattr(image, 'startswith') and image.startswith('http'):
|
||||
image = {'url': image}
|
||||
else:
|
||||
module.fail_json(
|
||||
msg="BearyChat doesn't have support for this kind of "
|
||||
"attachment image")
|
||||
target_images.append(image)
|
||||
return attachment
|
||||
|
||||
|
||||
def do_notify_bearychat(module, url, payload):
|
||||
response, info = fetch_url(module, url, data=payload)
|
||||
if info['status'] != 200:
|
||||
url_info = urlparse(url)
|
||||
obscured_incoming_webhook = urlunparse(
|
||||
(url_info.scheme, url_info.netloc, '[obscured]', '', '', ''))
|
||||
module.fail_json(
|
||||
msg=" failed to send %s to %s: %s" % (
|
||||
payload, obscured_incoming_webhook, info['msg']))
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(argument_spec={
|
||||
'url': dict(type='str', required=True, no_log=True),
|
||||
'text': dict(type='str'),
|
||||
'markdown': dict(default='yes', type='bool'),
|
||||
'channel': dict(type='str'),
|
||||
'attachments': dict(type='list'),
|
||||
})
|
||||
|
||||
if not HAS_URLPARSE:
|
||||
module.fail_json(msg='urlparse is not installed')
|
||||
|
||||
url = module.params['url']
|
||||
text = module.params['text']
|
||||
markdown = module.params['markdown']
|
||||
channel = module.params['channel']
|
||||
attachments = module.params['attachments']
|
||||
|
||||
payload = build_payload_for_bearychat(
|
||||
module, text, markdown, channel, attachments)
|
||||
do_notify_bearychat(module, url, payload)
|
||||
|
||||
module.exit_json(msg="OK")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
152
plugins/modules/notification/campfire.py
Normal file
152
plugins/modules/notification/campfire.py
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: campfire
|
||||
short_description: Send a message to Campfire
|
||||
description:
|
||||
- Send a message to Campfire.
|
||||
- Messages with newlines will result in a "Paste" message being sent.
|
||||
options:
|
||||
subscription:
|
||||
description:
|
||||
- The subscription name to use.
|
||||
required: true
|
||||
token:
|
||||
description:
|
||||
- API token.
|
||||
required: true
|
||||
room:
|
||||
description:
|
||||
- Room number to which the message should be sent.
|
||||
required: true
|
||||
msg:
|
||||
description:
|
||||
- The message body.
|
||||
required: true
|
||||
notify:
|
||||
description:
|
||||
- Send a notification sound before the message.
|
||||
required: false
|
||||
choices: ["56k", "bell", "bezos", "bueller", "clowntown",
|
||||
"cottoneyejoe", "crickets", "dadgummit", "dangerzone",
|
||||
"danielsan", "deeper", "drama", "greatjob", "greyjoy",
|
||||
"guarantee", "heygirl", "horn", "horror",
|
||||
"inconceivable", "live", "loggins", "makeitso", "noooo",
|
||||
"nyan", "ohmy", "ohyeah", "pushit", "rimshot",
|
||||
"rollout", "rumble", "sax", "secret", "sexyback",
|
||||
"story", "tada", "tmyk", "trololo", "trombone", "unix",
|
||||
"vuvuzela", "what", "whoomp", "yeah", "yodel"]
|
||||
|
||||
# informational: requirements for nodes
|
||||
requirements: [ ]
|
||||
author: "Adam Garside (@fabulops)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- campfire:
|
||||
subscription: foo
|
||||
token: 12345
|
||||
room: 123
|
||||
msg: Task completed.
|
||||
|
||||
- campfire:
|
||||
subscription: foo
|
||||
token: 12345
|
||||
room: 123
|
||||
notify: loggins
|
||||
msg: Task completed ... with feeling.
|
||||
'''
|
||||
|
||||
try:
|
||||
from html import escape as html_escape
|
||||
except ImportError:
|
||||
# Python-3.2 or later
|
||||
import cgi
|
||||
|
||||
def html_escape(text, quote=True):
|
||||
return cgi.escape(text, quote)
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
subscription=dict(required=True),
|
||||
token=dict(required=True, no_log=True),
|
||||
room=dict(required=True),
|
||||
msg=dict(required=True),
|
||||
notify=dict(required=False,
|
||||
choices=["56k", "bell", "bezos", "bueller",
|
||||
"clowntown", "cottoneyejoe",
|
||||
"crickets", "dadgummit", "dangerzone",
|
||||
"danielsan", "deeper", "drama",
|
||||
"greatjob", "greyjoy", "guarantee",
|
||||
"heygirl", "horn", "horror",
|
||||
"inconceivable", "live", "loggins",
|
||||
"makeitso", "noooo", "nyan", "ohmy",
|
||||
"ohyeah", "pushit", "rimshot",
|
||||
"rollout", "rumble", "sax", "secret",
|
||||
"sexyback", "story", "tada", "tmyk",
|
||||
"trololo", "trombone", "unix",
|
||||
"vuvuzela", "what", "whoomp", "yeah",
|
||||
"yodel"]),
|
||||
),
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
||||
subscription = module.params["subscription"]
|
||||
token = module.params["token"]
|
||||
room = module.params["room"]
|
||||
msg = module.params["msg"]
|
||||
notify = module.params["notify"]
|
||||
|
||||
URI = "https://%s.campfirenow.com" % subscription
|
||||
NSTR = "<message><type>SoundMessage</type><body>%s</body></message>"
|
||||
MSTR = "<message><body>%s</body></message>"
|
||||
AGENT = "Ansible/1.2"
|
||||
|
||||
# Hack to add basic auth username and password the way fetch_url expects
|
||||
module.params['url_username'] = token
|
||||
module.params['url_password'] = 'X'
|
||||
|
||||
target_url = '%s/room/%s/speak.xml' % (URI, room)
|
||||
headers = {'Content-Type': 'application/xml',
|
||||
'User-agent': AGENT}
|
||||
|
||||
# Send some audible notification if requested
|
||||
if notify:
|
||||
response, info = fetch_url(module, target_url, data=NSTR % html_escape(notify), headers=headers)
|
||||
if info['status'] not in [200, 201]:
|
||||
module.fail_json(msg="unable to send msg: '%s', campfire api"
|
||||
" returned error code: '%s'" %
|
||||
(notify, info['status']))
|
||||
|
||||
# Send the message
|
||||
response, info = fetch_url(module, target_url, data=MSTR % html_escape(msg), headers=headers)
|
||||
if info['status'] not in [200, 201]:
|
||||
module.fail_json(msg="unable to send msg: '%s', campfire api"
|
||||
" returned error code: '%s'" %
|
||||
(msg, info['status']))
|
||||
|
||||
module.exit_json(changed=True, room=room, msg=msg, notify=notify)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
151
plugins/modules/notification/catapult.py
Normal file
151
plugins/modules/notification/catapult.py
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2016, Jonathan Mainguy <jon@soh.re>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
#
|
||||
# basis of code taken from the ansible twillio and nexmo modules
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: catapult
|
||||
short_description: Send a sms / mms using the catapult bandwidth api
|
||||
description:
|
||||
- Allows notifications to be sent using sms / mms via the catapult bandwidth api.
|
||||
options:
|
||||
src:
|
||||
description:
|
||||
- One of your catapult telephone numbers the message should come from (must be in E.164 format, like C(+19195551212)).
|
||||
required: true
|
||||
dest:
|
||||
description:
|
||||
- The phone number or numbers the message should be sent to (must be in E.164 format, like C(+19195551212)).
|
||||
required: true
|
||||
msg:
|
||||
description:
|
||||
- The contents of the text message (must be 2048 characters or less).
|
||||
required: true
|
||||
media:
|
||||
description:
|
||||
- For MMS messages, a media url to the location of the media to be sent with the message.
|
||||
user_id:
|
||||
description:
|
||||
- User Id from Api account page.
|
||||
required: true
|
||||
api_token:
|
||||
description:
|
||||
- Api Token from Api account page.
|
||||
required: true
|
||||
api_secret:
|
||||
description:
|
||||
- Api Secret from Api account page.
|
||||
required: true
|
||||
|
||||
author: "Jonathan Mainguy (@Jmainguy)"
|
||||
notes:
|
||||
- Will return changed even if the media url is wrong.
|
||||
- Will return changed if the destination number is invalid.
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Send a mms to multiple users
|
||||
catapult:
|
||||
src: "+15035555555"
|
||||
dest:
|
||||
- "+12525089000"
|
||||
- "+12018994225"
|
||||
media: "http://example.com/foobar.jpg"
|
||||
msg: "Task is complete"
|
||||
user_id: "{{ user_id }}"
|
||||
api_token: "{{ api_token }}"
|
||||
api_secret: "{{ api_secret }}"
|
||||
|
||||
- name: Send a sms to a single user
|
||||
catapult:
|
||||
src: "+15035555555"
|
||||
dest: "+12018994225"
|
||||
msg: "Consider yourself notified"
|
||||
user_id: "{{ user_id }}"
|
||||
api_token: "{{ api_token }}"
|
||||
api_secret: "{{ api_secret }}"
|
||||
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
changed:
|
||||
description: Whether the api accepted the message.
|
||||
returned: always
|
||||
type: bool
|
||||
sample: True
|
||||
'''
|
||||
|
||||
|
||||
import json
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
def send(module, src, dest, msg, media, user_id, api_token, api_secret):
|
||||
"""
|
||||
Send the message
|
||||
"""
|
||||
AGENT = "Ansible"
|
||||
URI = "https://api.catapult.inetwork.com/v1/users/%s/messages" % user_id
|
||||
data = {'from': src, 'to': dest, 'text': msg}
|
||||
if media:
|
||||
data['media'] = media
|
||||
|
||||
headers = {'User-Agent': AGENT, 'Content-type': 'application/json'}
|
||||
|
||||
# Hack module params to have the Basic auth params that fetch_url expects
|
||||
module.params['url_username'] = api_token.replace('\n', '')
|
||||
module.params['url_password'] = api_secret.replace('\n', '')
|
||||
|
||||
return fetch_url(module, URI, data=json.dumps(data), headers=headers, method="post")
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
src=dict(required=True),
|
||||
dest=dict(required=True, type='list'),
|
||||
msg=dict(required=True),
|
||||
user_id=dict(required=True),
|
||||
api_token=dict(required=True, no_log=True),
|
||||
api_secret=dict(required=True, no_log=True),
|
||||
media=dict(default=None, required=False),
|
||||
),
|
||||
)
|
||||
|
||||
src = module.params['src']
|
||||
dest = module.params['dest']
|
||||
msg = module.params['msg']
|
||||
media = module.params['media']
|
||||
user_id = module.params['user_id']
|
||||
api_token = module.params['api_token']
|
||||
api_secret = module.params['api_secret']
|
||||
|
||||
for number in dest:
|
||||
rc, info = send(module, src, number, msg, media, user_id, api_token, api_secret)
|
||||
if info["status"] != 201:
|
||||
body = json.loads(info["body"])
|
||||
fail_msg = body["message"]
|
||||
module.fail_json(msg=fail_msg)
|
||||
|
||||
changed = True
|
||||
module.exit_json(changed=changed)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
191
plugins/modules/notification/cisco_spark.py
Normal file
191
plugins/modules/notification/cisco_spark.py
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: Ansible Project
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['stableinterface'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: cisco_spark
|
||||
short_description: Send a message to a Cisco Spark Room or Individual.
|
||||
description:
|
||||
- Send a message to a Cisco Spark Room or Individual with options to control the formatting.
|
||||
author: Drew Rusell (@drew-russell)
|
||||
notes:
|
||||
- The C(recipient_id) type must be valid for the supplied C(recipient_id).
|
||||
- Full API documentation can be found at U(https://developer.ciscospark.com/endpoint-messages-post.html).
|
||||
|
||||
options:
|
||||
|
||||
recipient_type:
|
||||
description:
|
||||
- The request parameter you would like to send the message to.
|
||||
- Messages can be sent to either a room or individual (by ID or E-Mail).
|
||||
required: True
|
||||
choices: ['roomId', 'toPersonEmail', 'toPersonId']
|
||||
|
||||
recipient_id:
|
||||
description:
|
||||
- The unique identifier associated with the supplied C(recipient_type).
|
||||
required: true
|
||||
|
||||
message_type:
|
||||
description:
|
||||
- Specifies how you would like the message formatted.
|
||||
required: False
|
||||
default: text
|
||||
choices: ['text', 'markdown']
|
||||
|
||||
personal_token:
|
||||
description:
|
||||
- Your personal access token required to validate the Spark API.
|
||||
required: true
|
||||
aliases: ['token']
|
||||
|
||||
message:
|
||||
description:
|
||||
- The message you would like to send.
|
||||
required: True
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
# Note: The following examples assume a variable file has been imported
|
||||
# that contains the appropriate information.
|
||||
|
||||
- name: Cisco Spark - Markdown Message to a Room
|
||||
cisco_spark:
|
||||
recipient_type: roomId
|
||||
recipient_id: "{{ room_id }}"
|
||||
message_type: markdown
|
||||
personal_token: "{{ token }}"
|
||||
message: "**Cisco Spark Ansible Module - Room Message in Markdown**"
|
||||
|
||||
- name: Cisco Spark - Text Message to a Room
|
||||
cisco_spark:
|
||||
recipient_type: roomId
|
||||
recipient_id: "{{ room_id }}"
|
||||
message_type: text
|
||||
personal_token: "{{ token }}"
|
||||
message: "Cisco Spark Ansible Module - Room Message in Text"
|
||||
|
||||
- name: Cisco Spark - Text Message by an Individuals ID
|
||||
cisco_spark:
|
||||
recipient_type: toPersonId
|
||||
recipient_id: "{{ person_id}}"
|
||||
message_type: text
|
||||
personal_token: "{{ token }}"
|
||||
message: "Cisco Spark Ansible Module - Text Message to Individual by ID"
|
||||
|
||||
- name: Cisco Spark - Text Message by an Individuals E-Mail Address
|
||||
cisco_spark:
|
||||
recipient_type: toPersonEmail
|
||||
recipient_id: "{{ person_email }}"
|
||||
message_type: text
|
||||
personal_token: "{{ token }}"
|
||||
message: "Cisco Spark Ansible Module - Text Message to Individual by E-Mail"
|
||||
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
status_code:
|
||||
description:
|
||||
- The Response Code returned by the Spark API.
|
||||
- Full Response Code explanations can be found at U(https://developer.ciscospark.com/endpoint-messages-post.html).
|
||||
returned: always
|
||||
type: int
|
||||
sample: 200
|
||||
|
||||
message:
|
||||
description:
|
||||
- The Response Message returned by the Spark API.
|
||||
- Full Response Code explanations can be found at U(https://developer.ciscospark.com/endpoint-messages-post.html).
|
||||
returned: always
|
||||
type: str
|
||||
sample: OK (585 bytes)
|
||||
"""
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
def spark_message(module):
|
||||
""" When check mode is specified, establish a read only connection, that does not return any user specific
|
||||
data, to validate connectivity. In regular mode, send a message to a Cisco Spark Room or Individual"""
|
||||
|
||||
# Ansible Specific Variables
|
||||
results = {}
|
||||
ansible = module.params
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer {0}'.format(ansible['personal_token']),
|
||||
'content-type': 'application/json'
|
||||
}
|
||||
|
||||
if module.check_mode:
|
||||
url = "https://api.ciscospark.com/v1/people/me"
|
||||
payload = None
|
||||
|
||||
else:
|
||||
url = "https://api.ciscospark.com/v1/messages"
|
||||
|
||||
payload = {
|
||||
ansible['recipient_type']: ansible['recipient_id'],
|
||||
ansible['message_type']: ansible['message']
|
||||
}
|
||||
|
||||
payload = module.jsonify(payload)
|
||||
|
||||
response, info = fetch_url(module, url, data=payload, headers=headers)
|
||||
|
||||
status_code = info['status']
|
||||
message = info['msg']
|
||||
|
||||
# Module will fail if the response is not 200
|
||||
if status_code != 200:
|
||||
results['failed'] = True
|
||||
results['status_code'] = status_code
|
||||
results['message'] = message
|
||||
else:
|
||||
results['failed'] = False
|
||||
results['status_code'] = status_code
|
||||
|
||||
if module.check_mode:
|
||||
results['message'] = 'Authentication Successful.'
|
||||
else:
|
||||
results['message'] = message
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def main():
|
||||
'''Ansible main. '''
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
recipient_type=dict(required=True, choices=[
|
||||
'roomId', 'toPersonEmail', 'toPersonId']),
|
||||
recipient_id=dict(required=True, no_log=True),
|
||||
message_type=dict(required=False, default=['text'], aliases=[
|
||||
'type'], choices=['text', 'markdown']),
|
||||
personal_token=dict(required=True, no_log=True, aliases=['token']),
|
||||
message=dict(required=True)
|
||||
|
||||
),
|
||||
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
results = spark_message(module)
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
188
plugins/modules/notification/flowdock.py
Normal file
188
plugins/modules/notification/flowdock.py
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2013 Matt Coddington <coddington@gmail.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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: flowdock
|
||||
author: "Matt Coddington (@mcodd)"
|
||||
short_description: Send a message to a flowdock
|
||||
description:
|
||||
- Send a message to a flowdock team inbox or chat using the push API (see https://www.flowdock.com/api/team-inbox and https://www.flowdock.com/api/chat)
|
||||
options:
|
||||
token:
|
||||
description:
|
||||
- API token.
|
||||
required: true
|
||||
type:
|
||||
description:
|
||||
- Whether to post to 'inbox' or 'chat'
|
||||
required: true
|
||||
choices: [ "inbox", "chat" ]
|
||||
msg:
|
||||
description:
|
||||
- Content of the message
|
||||
required: true
|
||||
tags:
|
||||
description:
|
||||
- tags of the message, separated by commas
|
||||
required: false
|
||||
external_user_name:
|
||||
description:
|
||||
- (chat only - required) Name of the "user" sending the message
|
||||
required: false
|
||||
from_address:
|
||||
description:
|
||||
- (inbox only - required) Email address of the message sender
|
||||
required: false
|
||||
source:
|
||||
description:
|
||||
- (inbox only - required) Human readable identifier of the application that uses the Flowdock API
|
||||
required: false
|
||||
subject:
|
||||
description:
|
||||
- (inbox only - required) Subject line of the message
|
||||
required: false
|
||||
from_name:
|
||||
description:
|
||||
- (inbox only) Name of the message sender
|
||||
required: false
|
||||
reply_to:
|
||||
description:
|
||||
- (inbox only) Email address for replies
|
||||
required: false
|
||||
project:
|
||||
description:
|
||||
- (inbox only) Human readable identifier for more detailed message categorization
|
||||
required: false
|
||||
link:
|
||||
description:
|
||||
- (inbox only) Link associated with the message. This will be used to link the message subject in Team Inbox.
|
||||
required: false
|
||||
validate_certs:
|
||||
description:
|
||||
- If C(no), SSL certificates will not be validated. This should only be used
|
||||
on personally controlled sites using self-signed certificates.
|
||||
required: false
|
||||
default: 'yes'
|
||||
type: bool
|
||||
|
||||
requirements: [ ]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- flowdock:
|
||||
type: inbox
|
||||
token: AAAAAA
|
||||
from_address: user@example.com
|
||||
source: my cool app
|
||||
msg: test from ansible
|
||||
subject: test subject
|
||||
|
||||
- flowdock:
|
||||
type: chat
|
||||
token: AAAAAA
|
||||
external_user_name: testuser
|
||||
msg: test from ansible
|
||||
tags: tag1,tag2,tag3
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Module execution.
|
||||
#
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
token=dict(required=True, no_log=True),
|
||||
msg=dict(required=True),
|
||||
type=dict(required=True, choices=["inbox", "chat"]),
|
||||
external_user_name=dict(required=False),
|
||||
from_address=dict(required=False),
|
||||
source=dict(required=False),
|
||||
subject=dict(required=False),
|
||||
from_name=dict(required=False),
|
||||
reply_to=dict(required=False),
|
||||
project=dict(required=False),
|
||||
tags=dict(required=False),
|
||||
link=dict(required=False),
|
||||
validate_certs=dict(default='yes', type='bool'),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
type = module.params["type"]
|
||||
token = module.params["token"]
|
||||
if type == 'inbox':
|
||||
url = "https://api.flowdock.com/v1/messages/team_inbox/%s" % (token)
|
||||
else:
|
||||
url = "https://api.flowdock.com/v1/messages/chat/%s" % (token)
|
||||
|
||||
params = {}
|
||||
|
||||
# required params
|
||||
params['content'] = module.params["msg"]
|
||||
|
||||
# required params for the 'chat' type
|
||||
if module.params['external_user_name']:
|
||||
if type == 'inbox':
|
||||
module.fail_json(msg="external_user_name is not valid for the 'inbox' type")
|
||||
else:
|
||||
params['external_user_name'] = module.params["external_user_name"]
|
||||
elif type == 'chat':
|
||||
module.fail_json(msg="external_user_name is required for the 'chat' type")
|
||||
|
||||
# required params for the 'inbox' type
|
||||
for item in ['from_address', 'source', 'subject']:
|
||||
if module.params[item]:
|
||||
if type == 'chat':
|
||||
module.fail_json(msg="%s is not valid for the 'chat' type" % item)
|
||||
else:
|
||||
params[item] = module.params[item]
|
||||
elif type == 'inbox':
|
||||
module.fail_json(msg="%s is required for the 'inbox' type" % item)
|
||||
|
||||
# optional params
|
||||
if module.params["tags"]:
|
||||
params['tags'] = module.params["tags"]
|
||||
|
||||
# optional params for the 'inbox' type
|
||||
for item in ['from_name', 'reply_to', 'project', 'link']:
|
||||
if module.params[item]:
|
||||
if type == 'chat':
|
||||
module.fail_json(msg="%s is not valid for the 'chat' type" % item)
|
||||
else:
|
||||
params[item] = module.params[item]
|
||||
|
||||
# If we're in check mode, just exit pretending like we succeeded
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=False)
|
||||
|
||||
# Send the data to Flowdock
|
||||
data = urlencode(params)
|
||||
response, info = fetch_url(module, url, data=data)
|
||||
if info['status'] != 200:
|
||||
module.fail_json(msg="unable to send msg: %s" % info['msg'])
|
||||
|
||||
module.exit_json(changed=True, msg=module.params["msg"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
115
plugins/modules/notification/grove.py
Normal file
115
plugins/modules/notification/grove.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: grove
|
||||
short_description: Sends a notification to a grove.io channel
|
||||
description:
|
||||
- The C(grove) module sends a message for a service to a Grove.io
|
||||
channel.
|
||||
options:
|
||||
channel_token:
|
||||
description:
|
||||
- Token of the channel to post to.
|
||||
required: true
|
||||
service:
|
||||
description:
|
||||
- Name of the service (displayed as the "user" in the message)
|
||||
required: false
|
||||
default: ansible
|
||||
message:
|
||||
description:
|
||||
- Message content
|
||||
required: true
|
||||
url:
|
||||
description:
|
||||
- Service URL for the web client
|
||||
required: false
|
||||
icon_url:
|
||||
description:
|
||||
- Icon for the service
|
||||
required: false
|
||||
validate_certs:
|
||||
description:
|
||||
- If C(no), SSL certificates will not be validated. This should only be used
|
||||
on personally controlled sites using self-signed certificates.
|
||||
default: 'yes'
|
||||
type: bool
|
||||
author: "Jonas Pfenniger (@zimbatm)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- grove: >
|
||||
channel_token=6Ph62VBBJOccmtTPZbubiPzdrhipZXtg
|
||||
service=my-app
|
||||
message=deployed {{ target }}
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
BASE_URL = 'https://grove.io/api/notice/%s/'
|
||||
|
||||
# ==============================================================
|
||||
# do_notify_grove
|
||||
|
||||
|
||||
def do_notify_grove(module, channel_token, service, message, url=None, icon_url=None):
|
||||
my_url = BASE_URL % (channel_token,)
|
||||
|
||||
my_data = dict(service=service, message=message)
|
||||
if url is not None:
|
||||
my_data['url'] = url
|
||||
if icon_url is not None:
|
||||
my_data['icon_url'] = icon_url
|
||||
|
||||
data = urlencode(my_data)
|
||||
response, info = fetch_url(module, my_url, data=data)
|
||||
if info['status'] != 200:
|
||||
module.fail_json(msg="failed to send notification: %s" % info['msg'])
|
||||
|
||||
# ==============================================================
|
||||
# main
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
channel_token=dict(type='str', required=True, no_log=True),
|
||||
message=dict(type='str', required=True),
|
||||
service=dict(type='str', default='ansible'),
|
||||
url=dict(type='str', default=None),
|
||||
icon_url=dict(type='str', default=None),
|
||||
validate_certs=dict(default='yes', type='bool'),
|
||||
)
|
||||
)
|
||||
|
||||
channel_token = module.params['channel_token']
|
||||
service = module.params['service']
|
||||
message = module.params['message']
|
||||
url = module.params['url']
|
||||
icon_url = module.params['icon_url']
|
||||
|
||||
do_notify_grove(module, channel_token, service, message, url, icon_url)
|
||||
|
||||
# Mission complete
|
||||
module.exit_json(msg="OK")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
208
plugins/modules/notification/hipchat.py
Normal file
208
plugins/modules/notification/hipchat.py
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['stableinterface'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: hipchat
|
||||
short_description: Send a message to Hipchat.
|
||||
description:
|
||||
- Send a message to a Hipchat room, with options to control the formatting.
|
||||
options:
|
||||
token:
|
||||
description:
|
||||
- API token.
|
||||
required: true
|
||||
room:
|
||||
description:
|
||||
- ID or name of the room.
|
||||
required: true
|
||||
from:
|
||||
description:
|
||||
- Name the message will appear to be sent from. Max length is 15
|
||||
characters - above this it will be truncated.
|
||||
default: Ansible
|
||||
msg:
|
||||
description:
|
||||
- The message body.
|
||||
required: true
|
||||
color:
|
||||
description:
|
||||
- Background color for the message.
|
||||
default: yellow
|
||||
choices: [ "yellow", "red", "green", "purple", "gray", "random" ]
|
||||
msg_format:
|
||||
description:
|
||||
- Message format.
|
||||
default: text
|
||||
choices: [ "text", "html" ]
|
||||
notify:
|
||||
description:
|
||||
- If true, a notification will be triggered for users in the room.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
validate_certs:
|
||||
description:
|
||||
- If C(no), SSL certificates will not be validated. This should only be used
|
||||
on personally controlled sites using self-signed certificates.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
api:
|
||||
description:
|
||||
- API url if using a self-hosted hipchat server. For Hipchat API version
|
||||
2 use the default URI with C(/v2) instead of C(/v1).
|
||||
default: 'https://api.hipchat.com/v1'
|
||||
|
||||
author:
|
||||
- Shirou Wakayama (@shirou)
|
||||
- Paul Bourdel (@pb8226)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- hipchat:
|
||||
room: notif
|
||||
msg: Ansible task finished
|
||||
|
||||
# Use Hipchat API version 2
|
||||
- hipchat:
|
||||
api: https://api.hipchat.com/v2/
|
||||
token: OAUTH2_TOKEN
|
||||
room: notify
|
||||
msg: Ansible task finished
|
||||
'''
|
||||
|
||||
# ===========================================
|
||||
# HipChat module specific support methods.
|
||||
#
|
||||
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.six.moves.urllib.request import pathname2url
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
DEFAULT_URI = "https://api.hipchat.com/v1"
|
||||
|
||||
MSG_URI_V1 = "/rooms/message"
|
||||
|
||||
NOTIFY_URI_V2 = "/room/{id_or_name}/notification"
|
||||
|
||||
|
||||
def send_msg_v1(module, token, room, msg_from, msg, msg_format='text',
|
||||
color='yellow', notify=False, api=MSG_URI_V1):
|
||||
'''sending message to hipchat v1 server'''
|
||||
|
||||
params = {}
|
||||
params['room_id'] = room
|
||||
params['from'] = msg_from[:15] # max length is 15
|
||||
params['message'] = msg
|
||||
params['message_format'] = msg_format
|
||||
params['color'] = color
|
||||
params['api'] = api
|
||||
params['notify'] = int(notify)
|
||||
|
||||
url = api + MSG_URI_V1 + "?auth_token=%s" % (token)
|
||||
data = urlencode(params)
|
||||
|
||||
if module.check_mode:
|
||||
# In check mode, exit before actually sending the message
|
||||
module.exit_json(changed=False)
|
||||
|
||||
response, info = fetch_url(module, url, data=data)
|
||||
if info['status'] == 200:
|
||||
return response.read()
|
||||
else:
|
||||
module.fail_json(msg="failed to send message, return status=%s" % str(info['status']))
|
||||
|
||||
|
||||
def send_msg_v2(module, token, room, msg_from, msg, msg_format='text',
|
||||
color='yellow', notify=False, api=NOTIFY_URI_V2):
|
||||
'''sending message to hipchat v2 server'''
|
||||
|
||||
headers = {'Authorization': 'Bearer %s' % token, 'Content-Type': 'application/json'}
|
||||
|
||||
body = dict()
|
||||
body['message'] = msg
|
||||
body['color'] = color
|
||||
body['message_format'] = msg_format
|
||||
body['notify'] = notify
|
||||
|
||||
POST_URL = api + NOTIFY_URI_V2
|
||||
|
||||
url = POST_URL.replace('{id_or_name}', pathname2url(room))
|
||||
data = json.dumps(body)
|
||||
|
||||
if module.check_mode:
|
||||
# In check mode, exit before actually sending the message
|
||||
module.exit_json(changed=False)
|
||||
|
||||
response, info = fetch_url(module, url, data=data, headers=headers, method='POST')
|
||||
|
||||
# https://www.hipchat.com/docs/apiv2/method/send_room_notification shows
|
||||
# 204 to be the expected result code.
|
||||
if info['status'] in [200, 204]:
|
||||
return response.read()
|
||||
else:
|
||||
module.fail_json(msg="failed to send message, return status=%s" % str(info['status']))
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Module execution.
|
||||
#
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
token=dict(required=True, no_log=True),
|
||||
room=dict(required=True),
|
||||
msg=dict(required=True),
|
||||
msg_from=dict(default="Ansible", aliases=['from']),
|
||||
color=dict(default="yellow", choices=["yellow", "red", "green",
|
||||
"purple", "gray", "random"]),
|
||||
msg_format=dict(default="text", choices=["text", "html"]),
|
||||
notify=dict(default=True, type='bool'),
|
||||
validate_certs=dict(default='yes', type='bool'),
|
||||
api=dict(default=DEFAULT_URI),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
token = module.params["token"]
|
||||
room = str(module.params["room"])
|
||||
msg = module.params["msg"]
|
||||
msg_from = module.params["msg_from"]
|
||||
color = module.params["color"]
|
||||
msg_format = module.params["msg_format"]
|
||||
notify = module.params["notify"]
|
||||
api = module.params["api"]
|
||||
|
||||
try:
|
||||
if api.find('/v2') != -1:
|
||||
send_msg_v2(module, token, room, msg_from, msg, msg_format, color, notify, api)
|
||||
else:
|
||||
send_msg_v1(module, token, room, msg_from, msg, msg_format, color, notify, api)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="unable to send msg: %s" % to_native(e), exception=traceback.format_exc())
|
||||
|
||||
changed = True
|
||||
module.exit_json(changed=changed, room=room, msg_from=msg_from, msg=msg)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
291
plugins/modules/notification/irc.py
Normal file
291
plugins/modules/notification/irc.py
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Jan-Piet Mens <jpmens () gmail.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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['stableinterface'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: irc
|
||||
short_description: Send a message to an IRC channel or a nick
|
||||
description:
|
||||
- Send a message to an IRC channel or a nick. This is a very simplistic implementation.
|
||||
options:
|
||||
server:
|
||||
description:
|
||||
- IRC server name/address
|
||||
default: localhost
|
||||
port:
|
||||
description:
|
||||
- IRC server port number
|
||||
default: 6667
|
||||
nick:
|
||||
description:
|
||||
- Nickname to send the message from. May be shortened, depending on server's NICKLEN setting.
|
||||
default: ansible
|
||||
msg:
|
||||
description:
|
||||
- The message body.
|
||||
required: true
|
||||
topic:
|
||||
description:
|
||||
- Set the channel topic
|
||||
color:
|
||||
description:
|
||||
- Text color for the message. ("none" is a valid option in 1.6 or later, in 1.6 and prior, the default color is black, not "none").
|
||||
Added 11 more colors in version 2.0.
|
||||
default: "none"
|
||||
choices: [ "none", "white", "black", "blue", "green", "red", "brown", "purple", "orange", "yellow", "light_green", "teal", "light_cyan",
|
||||
"light_blue", "pink", "gray", "light_gray"]
|
||||
channel:
|
||||
description:
|
||||
- Channel name. One of nick_to or channel needs to be set. When both are set, the message will be sent to both of them.
|
||||
required: true
|
||||
nick_to:
|
||||
description:
|
||||
- A list of nicknames to send the message to. One of nick_to or channel needs to be set. When both are defined, the message will be sent to both of them.
|
||||
key:
|
||||
description:
|
||||
- Channel key
|
||||
passwd:
|
||||
description:
|
||||
- Server password
|
||||
timeout:
|
||||
description:
|
||||
- Timeout to use while waiting for successful registration and join
|
||||
messages, this is to prevent an endless loop
|
||||
default: 30
|
||||
use_ssl:
|
||||
description:
|
||||
- Designates whether TLS/SSL should be used when connecting to the IRC server
|
||||
type: bool
|
||||
default: 'no'
|
||||
part:
|
||||
description:
|
||||
- Designates whether user should part from channel after sending message or not.
|
||||
Useful for when using a faux bot and not wanting join/parts between messages.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
style:
|
||||
description:
|
||||
- Text style for the message. Note italic does not work on some clients
|
||||
choices: [ "bold", "underline", "reverse", "italic" ]
|
||||
|
||||
# informational: requirements for nodes
|
||||
requirements: [ socket ]
|
||||
author:
|
||||
- "Jan-Piet Mens (@jpmens)"
|
||||
- "Matt Martz (@sivel)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- irc:
|
||||
server: irc.example.net
|
||||
channel: #t1
|
||||
msg: Hello world
|
||||
|
||||
- local_action:
|
||||
module: irc
|
||||
port: 6669
|
||||
server: irc.example.net
|
||||
channel: #t1
|
||||
msg: 'All finished at {{ ansible_date_time.iso8601 }}'
|
||||
color: red
|
||||
nick: ansibleIRC
|
||||
|
||||
- local_action:
|
||||
module: irc
|
||||
port: 6669
|
||||
server: irc.example.net
|
||||
channel: #t1
|
||||
nick_to:
|
||||
- nick1
|
||||
- nick2
|
||||
msg: 'All finished at {{ ansible_date_time.iso8601 }}'
|
||||
color: red
|
||||
nick: ansibleIRC
|
||||
'''
|
||||
|
||||
# ===========================================
|
||||
# IRC module support methods.
|
||||
#
|
||||
|
||||
import re
|
||||
import socket
|
||||
import ssl
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils._text import to_native, to_bytes
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def send_msg(msg, server='localhost', port='6667', channel=None, nick_to=None, key=None, topic=None,
|
||||
nick="ansible", color='none', passwd=False, timeout=30, use_ssl=False, part=True, style=None):
|
||||
'''send message to IRC'''
|
||||
nick_to = [] if nick_to is None else nick_to
|
||||
|
||||
colornumbers = {
|
||||
'white': "00",
|
||||
'black': "01",
|
||||
'blue': "02",
|
||||
'green': "03",
|
||||
'red': "04",
|
||||
'brown': "05",
|
||||
'purple': "06",
|
||||
'orange': "07",
|
||||
'yellow': "08",
|
||||
'light_green': "09",
|
||||
'teal': "10",
|
||||
'light_cyan': "11",
|
||||
'light_blue': "12",
|
||||
'pink': "13",
|
||||
'gray': "14",
|
||||
'light_gray': "15",
|
||||
}
|
||||
|
||||
stylechoices = {
|
||||
'bold': "\x02",
|
||||
'underline': "\x1F",
|
||||
'reverse': "\x16",
|
||||
'italic': "\x1D",
|
||||
}
|
||||
|
||||
try:
|
||||
styletext = stylechoices[style]
|
||||
except Exception:
|
||||
styletext = ""
|
||||
|
||||
try:
|
||||
colornumber = colornumbers[color]
|
||||
colortext = "\x03" + colornumber
|
||||
except Exception:
|
||||
colortext = ""
|
||||
|
||||
message = styletext + colortext + msg
|
||||
|
||||
irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
if use_ssl:
|
||||
irc = ssl.wrap_socket(irc)
|
||||
irc.connect((server, int(port)))
|
||||
|
||||
if passwd:
|
||||
irc.send(to_bytes('PASS %s\r\n' % passwd))
|
||||
irc.send(to_bytes('NICK %s\r\n' % nick))
|
||||
irc.send(to_bytes('USER %s %s %s :ansible IRC\r\n' % (nick, nick, nick)))
|
||||
motd = ''
|
||||
start = time.time()
|
||||
while 1:
|
||||
motd += to_native(irc.recv(1024))
|
||||
# The server might send back a shorter nick than we specified (due to NICKLEN),
|
||||
# so grab that and use it from now on (assuming we find the 00[1-4] response).
|
||||
match = re.search(r'^:\S+ 00[1-4] (?P<nick>\S+) :', motd, flags=re.M)
|
||||
if match:
|
||||
nick = match.group('nick')
|
||||
break
|
||||
elif time.time() - start > timeout:
|
||||
raise Exception('Timeout waiting for IRC server welcome response')
|
||||
time.sleep(0.5)
|
||||
|
||||
if channel:
|
||||
if key:
|
||||
irc.send(to_bytes('JOIN %s %s\r\n' % (channel, key)))
|
||||
else:
|
||||
irc.send(to_bytes('JOIN %s\r\n' % channel))
|
||||
|
||||
join = ''
|
||||
start = time.time()
|
||||
while 1:
|
||||
join += to_native(irc.recv(1024))
|
||||
if re.search(r'^:\S+ 366 %s %s :' % (nick, channel), join, flags=re.M | re.I):
|
||||
break
|
||||
elif time.time() - start > timeout:
|
||||
raise Exception('Timeout waiting for IRC JOIN response')
|
||||
time.sleep(0.5)
|
||||
|
||||
if topic is not None:
|
||||
irc.send(to_bytes('TOPIC %s :%s\r\n' % (channel, topic)))
|
||||
time.sleep(1)
|
||||
|
||||
if nick_to:
|
||||
for nick in nick_to:
|
||||
irc.send(to_bytes('PRIVMSG %s :%s\r\n' % (nick, message)))
|
||||
if channel:
|
||||
irc.send(to_bytes('PRIVMSG %s :%s\r\n' % (channel, message)))
|
||||
time.sleep(1)
|
||||
if part:
|
||||
if channel:
|
||||
irc.send(to_bytes('PART %s\r\n' % channel))
|
||||
irc.send(to_bytes('QUIT\r\n'))
|
||||
time.sleep(1)
|
||||
irc.close()
|
||||
|
||||
# ===========================================
|
||||
# Main
|
||||
#
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
server=dict(default='localhost'),
|
||||
port=dict(type='int', default=6667),
|
||||
nick=dict(default='ansible'),
|
||||
nick_to=dict(required=False, type='list'),
|
||||
msg=dict(required=True),
|
||||
color=dict(default="none", aliases=['colour'], choices=["white", "black", "blue",
|
||||
"green", "red", "brown",
|
||||
"purple", "orange", "yellow",
|
||||
"light_green", "teal", "light_cyan",
|
||||
"light_blue", "pink", "gray",
|
||||
"light_gray", "none"]),
|
||||
style=dict(default="none", choices=["underline", "reverse", "bold", "italic", "none"]),
|
||||
channel=dict(required=False),
|
||||
key=dict(no_log=True),
|
||||
topic=dict(),
|
||||
passwd=dict(no_log=True),
|
||||
timeout=dict(type='int', default=30),
|
||||
part=dict(type='bool', default=True),
|
||||
use_ssl=dict(type='bool', default=False)
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[['channel', 'nick_to']]
|
||||
)
|
||||
|
||||
server = module.params["server"]
|
||||
port = module.params["port"]
|
||||
nick = module.params["nick"]
|
||||
nick_to = module.params["nick_to"]
|
||||
msg = module.params["msg"]
|
||||
color = module.params["color"]
|
||||
channel = module.params["channel"]
|
||||
topic = module.params["topic"]
|
||||
if topic and not channel:
|
||||
module.fail_json(msg="When topic is specified, a channel is required.")
|
||||
key = module.params["key"]
|
||||
passwd = module.params["passwd"]
|
||||
timeout = module.params["timeout"]
|
||||
use_ssl = module.params["use_ssl"]
|
||||
part = module.params["part"]
|
||||
style = module.params["style"]
|
||||
|
||||
try:
|
||||
send_msg(msg, server, port, channel, nick_to, key, topic, nick, color, passwd, timeout, use_ssl, part, style)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="unable to send to IRC: %s" % to_native(e), exception=traceback.format_exc())
|
||||
|
||||
module.exit_json(changed=False, channel=channel, nick=nick,
|
||||
msg=msg)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
164
plugins/modules/notification/jabber.py
Normal file
164
plugins/modules/notification/jabber.py
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# (c) 2015, Brian Coca <bcoca@ansible.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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['stableinterface'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: jabber
|
||||
short_description: Send a message to jabber user or chat room
|
||||
description:
|
||||
- Send a message to jabber
|
||||
options:
|
||||
user:
|
||||
description:
|
||||
- User as which to connect
|
||||
required: true
|
||||
password:
|
||||
description:
|
||||
- password for user to connect
|
||||
required: true
|
||||
to:
|
||||
description:
|
||||
- user ID or name of the room, when using room use a slash to indicate your nick.
|
||||
required: true
|
||||
msg:
|
||||
description:
|
||||
- The message body.
|
||||
required: true
|
||||
host:
|
||||
description:
|
||||
- host to connect, overrides user info
|
||||
port:
|
||||
description:
|
||||
- port to connect to, overrides default
|
||||
default: 5222
|
||||
encoding:
|
||||
description:
|
||||
- message encoding
|
||||
|
||||
# informational: requirements for nodes
|
||||
requirements:
|
||||
- python xmpp (xmpppy)
|
||||
author: "Brian Coca (@bcoca)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# send a message to a user
|
||||
- jabber:
|
||||
user: mybot@example.net
|
||||
password: secret
|
||||
to: friend@example.net
|
||||
msg: Ansible task finished
|
||||
|
||||
# send a message to a room
|
||||
- jabber:
|
||||
user: mybot@example.net
|
||||
password: secret
|
||||
to: mychaps@conference.example.net/ansiblebot
|
||||
msg: Ansible task finished
|
||||
|
||||
# send a message, specifying the host and port
|
||||
- jabber:
|
||||
user: mybot@example.net
|
||||
host: talk.example.net
|
||||
port: 5223
|
||||
password: secret
|
||||
to: mychaps@example.net
|
||||
msg: Ansible task finished
|
||||
'''
|
||||
|
||||
import time
|
||||
import traceback
|
||||
|
||||
HAS_XMPP = True
|
||||
XMPP_IMP_ERR = None
|
||||
try:
|
||||
import xmpp
|
||||
except ImportError:
|
||||
XMPP_IMP_ERR = traceback.format_exc()
|
||||
HAS_XMPP = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
user=dict(required=True),
|
||||
password=dict(required=True, no_log=True),
|
||||
to=dict(required=True),
|
||||
msg=dict(required=True),
|
||||
host=dict(required=False),
|
||||
port=dict(required=False, default=5222, type='int'),
|
||||
encoding=dict(required=False),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
if not HAS_XMPP:
|
||||
module.fail_json(msg=missing_required_lib('xmpppy'), exception=XMPP_IMP_ERR)
|
||||
|
||||
jid = xmpp.JID(module.params['user'])
|
||||
user = jid.getNode()
|
||||
server = jid.getDomain()
|
||||
port = module.params['port']
|
||||
password = module.params['password']
|
||||
try:
|
||||
to, nick = module.params['to'].split('/', 1)
|
||||
except ValueError:
|
||||
to, nick = module.params['to'], None
|
||||
|
||||
if module.params['host']:
|
||||
host = module.params['host']
|
||||
else:
|
||||
host = server
|
||||
if module.params['encoding']:
|
||||
xmpp.simplexml.ENCODING = module.params['encoding']
|
||||
|
||||
msg = xmpp.protocol.Message(body=module.params['msg'])
|
||||
|
||||
try:
|
||||
conn = xmpp.Client(server, debug=[])
|
||||
if not conn.connect(server=(host, port)):
|
||||
module.fail_json(rc=1, msg='Failed to connect to server: %s' % (server))
|
||||
if not conn.auth(user, password, 'Ansible'):
|
||||
module.fail_json(rc=1, msg='Failed to authorize %s on: %s' % (user, server))
|
||||
# some old servers require this, also the sleep following send
|
||||
conn.sendInitPresence(requestRoster=0)
|
||||
|
||||
if nick: # sending to room instead of user, need to join
|
||||
msg.setType('groupchat')
|
||||
msg.setTag('x', namespace='http://jabber.org/protocol/muc#user')
|
||||
join = xmpp.Presence(to=module.params['to'])
|
||||
join.setTag('x', namespace='http://jabber.org/protocol/muc')
|
||||
conn.send(join)
|
||||
time.sleep(1)
|
||||
else:
|
||||
msg.setType('chat')
|
||||
|
||||
msg.setTo(to)
|
||||
if not module.check_mode:
|
||||
conn.send(msg)
|
||||
time.sleep(1)
|
||||
conn.disconnect()
|
||||
except Exception as e:
|
||||
module.fail_json(msg="unable to send msg: %s" % to_native(e), exception=traceback.format_exc())
|
||||
|
||||
module.exit_json(changed=False, to=to, user=user, msg=msg.getBody())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
98
plugins/modules/notification/logentries_msg.py
Normal file
98
plugins/modules/notification/logentries_msg.py
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'status': ['preview'],
|
||||
'supported_by': 'community',
|
||||
'metadata_version': '1.1'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: logentries_msg
|
||||
short_description: Send a message to logentries.
|
||||
description:
|
||||
- Send a message to logentries
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
options:
|
||||
token:
|
||||
description:
|
||||
- Log token.
|
||||
required: true
|
||||
msg:
|
||||
description:
|
||||
- The message body.
|
||||
required: true
|
||||
api:
|
||||
description:
|
||||
- API endpoint
|
||||
default: data.logentries.com
|
||||
port:
|
||||
description:
|
||||
- API endpoint port
|
||||
default: 80
|
||||
author: "Jimmy Tang (@jcftang) <jimmy_tang@rapid7.com>"
|
||||
'''
|
||||
|
||||
RETURN = '''# '''
|
||||
|
||||
EXAMPLES = '''
|
||||
- logentries_msg:
|
||||
token=00000000-0000-0000-0000-000000000000
|
||||
msg="{{ ansible_hostname }}"
|
||||
'''
|
||||
|
||||
import socket
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def send_msg(module, token, msg, api, port):
|
||||
|
||||
message = "{0} {1}\n".format(token, msg)
|
||||
|
||||
api_ip = socket.gethostbyname(api)
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((api_ip, port))
|
||||
try:
|
||||
if not module.check_mode:
|
||||
s.send(message)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="failed to send message, msg=%s" % e)
|
||||
s.close()
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
token=dict(type='str', required=True),
|
||||
msg=dict(type='str', required=True),
|
||||
api=dict(type='str', default="data.logentries.com"),
|
||||
port=dict(type='int', default=80)),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
token = module.params["token"]
|
||||
msg = module.params["msg"]
|
||||
api = module.params["api"]
|
||||
port = module.params["port"]
|
||||
|
||||
changed = False
|
||||
try:
|
||||
send_msg(module, token, msg, api, port)
|
||||
changed = True
|
||||
except Exception as e:
|
||||
module.fail_json(msg="unable to send msg: %s" % e)
|
||||
|
||||
module.exit_json(changed=changed, msg=msg)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
389
plugins/modules/notification/mail.py
Normal file
389
plugins/modules/notification/mail.py
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2012, Dag Wieers (@dagwieers) <dag@wieers.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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['stableinterface'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
author:
|
||||
- Dag Wieers (@dagwieers)
|
||||
module: mail
|
||||
short_description: Send an email
|
||||
description:
|
||||
- This module is useful for sending emails from playbooks.
|
||||
- One may wonder why automate sending emails? In complex environments
|
||||
there are from time to time processes that cannot be automated, either
|
||||
because you lack the authority to make it so, or because not everyone
|
||||
agrees to a common approach.
|
||||
- If you cannot automate a specific step, but the step is non-blocking,
|
||||
sending out an email to the responsible party to make them perform their
|
||||
part of the bargain is an elegant way to put the responsibility in
|
||||
someone else's lap.
|
||||
- Of course sending out a mail can be equally useful as a way to notify
|
||||
one or more people in a team that a specific action has been
|
||||
(successfully) taken.
|
||||
options:
|
||||
from:
|
||||
description:
|
||||
- The email-address the mail is sent from. May contain address and phrase.
|
||||
type: str
|
||||
default: root
|
||||
to:
|
||||
description:
|
||||
- The email-address(es) the mail is being sent to.
|
||||
- This is a list, which may contain address and phrase portions.
|
||||
type: list
|
||||
default: root
|
||||
aliases: [ recipients ]
|
||||
cc:
|
||||
description:
|
||||
- The email-address(es) the mail is being copied to.
|
||||
- This is a list, which may contain address and phrase portions.
|
||||
type: list
|
||||
bcc:
|
||||
description:
|
||||
- The email-address(es) the mail is being 'blind' copied to.
|
||||
- This is a list, which may contain address and phrase portions.
|
||||
type: list
|
||||
subject:
|
||||
description:
|
||||
- The subject of the email being sent.
|
||||
required: yes
|
||||
type: str
|
||||
body:
|
||||
description:
|
||||
- The body of the email being sent.
|
||||
type: str
|
||||
default: $subject
|
||||
username:
|
||||
description:
|
||||
- If SMTP requires username.
|
||||
type: str
|
||||
password:
|
||||
description:
|
||||
- If SMTP requires password.
|
||||
type: str
|
||||
host:
|
||||
description:
|
||||
- The mail server.
|
||||
type: str
|
||||
default: localhost
|
||||
port:
|
||||
description:
|
||||
- The mail server port.
|
||||
- This must be a valid integer between 1 and 65534
|
||||
type: int
|
||||
default: 25
|
||||
attach:
|
||||
description:
|
||||
- A list of pathnames of files to attach to the message.
|
||||
- Attached files will have their content-type set to C(application/octet-stream).
|
||||
type: list
|
||||
default: []
|
||||
headers:
|
||||
description:
|
||||
- A list of headers which should be added to the message.
|
||||
- Each individual header is specified as C(header=value) (see example below).
|
||||
type: list
|
||||
default: []
|
||||
charset:
|
||||
description:
|
||||
- The character set of email being sent.
|
||||
type: str
|
||||
default: utf-8
|
||||
subtype:
|
||||
description:
|
||||
- The minor mime type, can be either C(plain) or C(html).
|
||||
- The major type is always C(text).
|
||||
type: str
|
||||
choices: [ html, plain ]
|
||||
default: plain
|
||||
secure:
|
||||
description:
|
||||
- If C(always), the connection will only send email if the connection is Encrypted.
|
||||
If the server doesn't accept the encrypted connection it will fail.
|
||||
- If C(try), the connection will attempt to setup a secure SSL/TLS session, before trying to send.
|
||||
- If C(never), the connection will not attempt to setup a secure SSL/TLS session, before sending
|
||||
- If C(starttls), the connection will try to upgrade to a secure SSL/TLS connection, before sending.
|
||||
If it is unable to do so it will fail.
|
||||
type: str
|
||||
choices: [ always, never, starttls, try ]
|
||||
default: try
|
||||
timeout:
|
||||
description:
|
||||
- Sets the timeout in seconds for connection attempts.
|
||||
type: int
|
||||
default: 20
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Example playbook sending mail to root
|
||||
mail:
|
||||
subject: System {{ ansible_hostname }} has been successfully provisioned.
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Sending an e-mail using Gmail SMTP servers
|
||||
mail:
|
||||
host: smtp.gmail.com
|
||||
port: 587
|
||||
username: username@gmail.com
|
||||
password: mysecret
|
||||
to: John Smith <john.smith@example.com>
|
||||
subject: Ansible-report
|
||||
body: System {{ ansible_hostname }} has been successfully provisioned.
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Send e-mail to a bunch of users, attaching files
|
||||
mail:
|
||||
host: 127.0.0.1
|
||||
port: 2025
|
||||
subject: Ansible-report
|
||||
body: Hello, this is an e-mail. I hope you like it ;-)
|
||||
from: jane@example.net (Jane Jolie)
|
||||
to:
|
||||
- John Doe <j.d@example.org>
|
||||
- Suzie Something <sue@example.com>
|
||||
cc: Charlie Root <root@localhost>
|
||||
attach:
|
||||
- /etc/group
|
||||
- /tmp/avatar2.png
|
||||
headers:
|
||||
- Reply-To=john@example.com
|
||||
- X-Special="Something or other"
|
||||
charset: us-ascii
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Sending an e-mail using the remote machine, not the Ansible controller node
|
||||
mail:
|
||||
host: localhost
|
||||
port: 25
|
||||
to: John Smith <john.smith@example.com>
|
||||
subject: Ansible-report
|
||||
body: System {{ ansible_hostname }} has been successfully provisioned.
|
||||
|
||||
- name: Sending an e-mail using Legacy SSL to the remote machine
|
||||
mail:
|
||||
host: localhost
|
||||
port: 25
|
||||
to: John Smith <john.smith@example.com>
|
||||
subject: Ansible-report
|
||||
body: System {{ ansible_hostname }} has been successfully provisioned.
|
||||
secure: always
|
||||
|
||||
- name: Sending an e-mail using StartTLS to the remote machine
|
||||
mail:
|
||||
host: localhost
|
||||
port: 25
|
||||
to: John Smith <john.smith@example.com>
|
||||
subject: Ansible-report
|
||||
body: System {{ ansible_hostname }} has been successfully provisioned.
|
||||
secure: starttls
|
||||
'''
|
||||
|
||||
import os
|
||||
import smtplib
|
||||
import ssl
|
||||
import traceback
|
||||
from email import encoders
|
||||
from email.utils import parseaddr, formataddr, formatdate
|
||||
from email.mime.base import MIMEBase
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.header import Header
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import PY3
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
username=dict(type='str'),
|
||||
password=dict(type='str', no_log=True),
|
||||
host=dict(type='str', default='localhost'),
|
||||
port=dict(type='int', default=25),
|
||||
sender=dict(type='str', default='root', aliases=['from']),
|
||||
to=dict(type='list', default=['root'], aliases=['recipients']),
|
||||
cc=dict(type='list', default=[]),
|
||||
bcc=dict(type='list', default=[]),
|
||||
subject=dict(type='str', required=True, aliases=['msg']),
|
||||
body=dict(type='str'),
|
||||
attach=dict(type='list', default=[]),
|
||||
headers=dict(type='list', default=[]),
|
||||
charset=dict(type='str', default='utf-8'),
|
||||
subtype=dict(type='str', default='plain', choices=['html', 'plain']),
|
||||
secure=dict(type='str', default='try', choices=['always', 'never', 'starttls', 'try']),
|
||||
timeout=dict(type='int', default=20),
|
||||
),
|
||||
required_together=[['password', 'username']],
|
||||
)
|
||||
|
||||
username = module.params.get('username')
|
||||
password = module.params.get('password')
|
||||
host = module.params.get('host')
|
||||
port = module.params.get('port')
|
||||
sender = module.params.get('sender')
|
||||
recipients = module.params.get('to')
|
||||
copies = module.params.get('cc')
|
||||
blindcopies = module.params.get('bcc')
|
||||
subject = module.params.get('subject')
|
||||
body = module.params.get('body')
|
||||
attach_files = module.params.get('attach')
|
||||
headers = module.params.get('headers')
|
||||
charset = module.params.get('charset')
|
||||
subtype = module.params.get('subtype')
|
||||
secure = module.params.get('secure')
|
||||
timeout = module.params.get('timeout')
|
||||
|
||||
code = 0
|
||||
secure_state = False
|
||||
sender_phrase, sender_addr = parseaddr(sender)
|
||||
|
||||
if not body:
|
||||
body = subject
|
||||
|
||||
try:
|
||||
if secure != 'never':
|
||||
try:
|
||||
if PY3:
|
||||
smtp = smtplib.SMTP_SSL(host=host, port=port, timeout=timeout)
|
||||
else:
|
||||
smtp = smtplib.SMTP_SSL(timeout=timeout)
|
||||
code, smtpmessage = smtp.connect(host, port)
|
||||
secure_state = True
|
||||
except ssl.SSLError as e:
|
||||
if secure == 'always':
|
||||
module.fail_json(rc=1, msg='Unable to start an encrypted session to %s:%s: %s' %
|
||||
(host, port, to_native(e)), exception=traceback.format_exc())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not secure_state:
|
||||
if PY3:
|
||||
smtp = smtplib.SMTP(host=host, port=port, timeout=timeout)
|
||||
else:
|
||||
smtp = smtplib.SMTP(timeout=timeout)
|
||||
code, smtpmessage = smtp.connect(host, port)
|
||||
|
||||
except smtplib.SMTPException as e:
|
||||
module.fail_json(rc=1, msg='Unable to Connect %s:%s: %s' % (host, port, to_native(e)), exception=traceback.format_exc())
|
||||
|
||||
try:
|
||||
smtp.ehlo()
|
||||
except smtplib.SMTPException as e:
|
||||
module.fail_json(rc=1, msg='Helo failed for host %s:%s: %s' % (host, port, to_native(e)), exception=traceback.format_exc())
|
||||
|
||||
if int(code) > 0:
|
||||
if not secure_state and secure in ('starttls', 'try'):
|
||||
if smtp.has_extn('STARTTLS'):
|
||||
try:
|
||||
smtp.starttls()
|
||||
secure_state = True
|
||||
except smtplib.SMTPException as e:
|
||||
module.fail_json(rc=1, msg='Unable to start an encrypted session to %s:%s: %s' %
|
||||
(host, port, to_native(e)), exception=traceback.format_exc())
|
||||
try:
|
||||
smtp.ehlo()
|
||||
except smtplib.SMTPException as e:
|
||||
module.fail_json(rc=1, msg='Helo failed for host %s:%s: %s' % (host, port, to_native(e)), exception=traceback.format_exc())
|
||||
else:
|
||||
if secure == 'starttls':
|
||||
module.fail_json(rc=1, msg='StartTLS is not offered on server %s:%s' % (host, port))
|
||||
|
||||
if username and password:
|
||||
if smtp.has_extn('AUTH'):
|
||||
try:
|
||||
smtp.login(username, password)
|
||||
except smtplib.SMTPAuthenticationError:
|
||||
module.fail_json(rc=1, msg='Authentication to %s:%s failed, please check your username and/or password' % (host, port))
|
||||
except smtplib.SMTPException:
|
||||
module.fail_json(rc=1, msg='No Suitable authentication method was found on %s:%s' % (host, port))
|
||||
else:
|
||||
module.fail_json(rc=1, msg="No Authentication on the server at %s:%s" % (host, port))
|
||||
|
||||
if not secure_state and (username and password):
|
||||
module.warn('Username and Password was sent without encryption')
|
||||
|
||||
msg = MIMEMultipart(_charset=charset)
|
||||
msg['From'] = formataddr((sender_phrase, sender_addr))
|
||||
msg['Date'] = formatdate(localtime=True)
|
||||
msg['Subject'] = Header(subject, charset)
|
||||
msg.preamble = "Multipart message"
|
||||
|
||||
for header in headers:
|
||||
# NOTE: Backward compatible with old syntax using '|' as delimiter
|
||||
for hdr in [x.strip() for x in header.split('|')]:
|
||||
try:
|
||||
h_key, h_val = hdr.split('=')
|
||||
h_val = to_native(Header(h_val, charset))
|
||||
msg.add_header(h_key, h_val)
|
||||
except Exception:
|
||||
module.warn("Skipping header '%s', unable to parse" % hdr)
|
||||
|
||||
if 'X-Mailer' not in msg:
|
||||
msg.add_header('X-Mailer', 'Ansible mail module')
|
||||
|
||||
addr_list = []
|
||||
for addr in [x.strip() for x in blindcopies]:
|
||||
addr_list.append(parseaddr(addr)[1]) # address only, w/o phrase
|
||||
|
||||
to_list = []
|
||||
for addr in [x.strip() for x in recipients]:
|
||||
to_list.append(formataddr(parseaddr(addr)))
|
||||
addr_list.append(parseaddr(addr)[1]) # address only, w/o phrase
|
||||
msg['To'] = ", ".join(to_list)
|
||||
|
||||
cc_list = []
|
||||
for addr in [x.strip() for x in copies]:
|
||||
cc_list.append(formataddr(parseaddr(addr)))
|
||||
addr_list.append(parseaddr(addr)[1]) # address only, w/o phrase
|
||||
msg['Cc'] = ", ".join(cc_list)
|
||||
|
||||
part = MIMEText(body + "\n\n", _subtype=subtype, _charset=charset)
|
||||
msg.attach(part)
|
||||
|
||||
# NOTE: Backware compatibility with old syntax using space as delimiter is not retained
|
||||
# This breaks files with spaces in it :-(
|
||||
for filename in attach_files:
|
||||
try:
|
||||
part = MIMEBase('application', 'octet-stream')
|
||||
with open(filename, 'rb') as fp:
|
||||
part.set_payload(fp.read())
|
||||
encoders.encode_base64(part)
|
||||
part.add_header('Content-disposition', 'attachment', filename=os.path.basename(filename))
|
||||
msg.attach(part)
|
||||
except Exception as e:
|
||||
module.fail_json(rc=1, msg="Failed to send mail: can't attach file %s: %s" %
|
||||
(filename, to_native(e)), exception=traceback.format_exc())
|
||||
|
||||
composed = msg.as_string()
|
||||
|
||||
try:
|
||||
result = smtp.sendmail(sender_addr, set(addr_list), composed)
|
||||
except Exception as e:
|
||||
module.fail_json(rc=1, msg="Failed to send mail to '%s': %s" %
|
||||
(", ".join(set(addr_list)), to_native(e)), exception=traceback.format_exc())
|
||||
|
||||
smtp.quit()
|
||||
|
||||
if result:
|
||||
for key in result:
|
||||
module.warn("Failed to send mail to '%s': %s %s" % (key, result[key][0], result[key][1]))
|
||||
module.exit_json(msg='Failed to send mail to at least one recipient', result=result)
|
||||
|
||||
module.exit_json(msg='Mail sent successfully', result=result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
138
plugins/modules/notification/matrix.py
Normal file
138
plugins/modules/notification/matrix.py
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
|
||||
# (c) 2018, Jan Christian Grünhage <jan.christian@gruenhage.xyz>
|
||||
# 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
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
author: "Jan Christian Grünhage (@jcgruenhage)"
|
||||
module: matrix
|
||||
short_description: Send notifications to matrix
|
||||
description:
|
||||
- This module sends html formatted notifications to matrix rooms.
|
||||
options:
|
||||
msg_plain:
|
||||
description:
|
||||
- Plain text form of the message to send to matrix, usually markdown
|
||||
required: true
|
||||
msg_html:
|
||||
description:
|
||||
- HTML form of the message to send to matrix
|
||||
required: true
|
||||
room_id:
|
||||
description:
|
||||
- ID of the room to send the notification to
|
||||
required: true
|
||||
hs_url:
|
||||
description:
|
||||
- URL of the homeserver, where the CS-API is reachable
|
||||
required: true
|
||||
token:
|
||||
description:
|
||||
- Authentication token for the API call. If provided, user_id and password are not required
|
||||
user_id:
|
||||
description:
|
||||
- The user id of the user
|
||||
password:
|
||||
description:
|
||||
- The password to log in with
|
||||
requirements:
|
||||
- matrix-client (Python library)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Send matrix notification with token
|
||||
matrix:
|
||||
msg_plain: "**hello world**"
|
||||
msg_html: "<b>hello world</b>"
|
||||
room_id: "!12345678:server.tld"
|
||||
hs_url: "https://matrix.org"
|
||||
token: "{{ matrix_auth_token }}"
|
||||
|
||||
- name: Send matrix notification with user_id and password
|
||||
matrix:
|
||||
msg_plain: "**hello world**"
|
||||
msg_html: "<b>hello world</b>"
|
||||
room_id: "!12345678:server.tld"
|
||||
hs_url: "https://matrix.org"
|
||||
user_id: "ansible_notification_bot"
|
||||
password: "{{ matrix_auth_password }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
MATRIX_IMP_ERR = None
|
||||
try:
|
||||
from matrix_client.client import MatrixClient
|
||||
except ImportError:
|
||||
MATRIX_IMP_ERR = traceback.format_exc()
|
||||
matrix_found = False
|
||||
else:
|
||||
matrix_found = True
|
||||
|
||||
|
||||
def run_module():
|
||||
module_args = dict(
|
||||
msg_plain=dict(type='str', required=True),
|
||||
msg_html=dict(type='str', required=True),
|
||||
room_id=dict(type='str', required=True),
|
||||
hs_url=dict(type='str', required=True),
|
||||
token=dict(type='str', required=False, no_log=True),
|
||||
user_id=dict(type='str', required=False),
|
||||
password=dict(type='str', required=False, no_log=True),
|
||||
)
|
||||
|
||||
result = dict(
|
||||
changed=False,
|
||||
message=''
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
mutually_exclusive=[['password', 'token']],
|
||||
required_one_of=[['password', 'token']],
|
||||
required_together=[['user_id', 'password']],
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
if not matrix_found:
|
||||
module.fail_json(msg=missing_required_lib('matrix-client'), exception=MATRIX_IMP_ERR)
|
||||
|
||||
if module.check_mode:
|
||||
return result
|
||||
|
||||
# create a client object
|
||||
client = MatrixClient(module.params['hs_url'])
|
||||
if module.params['token'] is not None:
|
||||
client.api.token = module.params['token']
|
||||
else:
|
||||
client.login(module.params['user_id'], module.params['password'], sync=False)
|
||||
|
||||
# make sure we are in a given room and return a room object for it
|
||||
room = client.join_room(module.params['room_id'])
|
||||
# send an html formatted messages
|
||||
room.send_html(module.params['msg_html'], module.params['msg_plain'])
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
run_module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
150
plugins/modules/notification/mattermost.py
Normal file
150
plugins/modules/notification/mattermost.py
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Benjamin Jolivot <bjolivot@gmail.com>
|
||||
# Inspired by slack module :
|
||||
# # (c) 2017, Steve Pletcher <steve@steve-pletcher.com>
|
||||
# # (c) 2016, René Moser <mail@renemoser.net>
|
||||
# # (c) 2015, Stefan Berggren <nsg@nsg.cc>
|
||||
# # (c) 2014, Ramon de la Fuente <ramon@delafuente.nl>)
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: mattermost
|
||||
short_description: Send Mattermost notifications
|
||||
description:
|
||||
- Sends notifications to U(http://your.mattermost.url) via the Incoming WebHook integration.
|
||||
author: "Benjamin Jolivot (@bjolivot)"
|
||||
options:
|
||||
url:
|
||||
description:
|
||||
- Mattermost url (i.e. http://mattermost.yourcompany.com).
|
||||
required: true
|
||||
api_key:
|
||||
description:
|
||||
- Mattermost webhook api key. Log into your mattermost site, go to
|
||||
Menu -> Integration -> Incoming Webhook -> Add Incoming Webhook.
|
||||
This will give you full URL. api_key is the last part.
|
||||
http://mattermost.example.com/hooks/C(API_KEY)
|
||||
required: true
|
||||
text:
|
||||
description:
|
||||
- Text to send. Note that the module does not handle escaping characters.
|
||||
required: true
|
||||
channel:
|
||||
description:
|
||||
- Channel to send the message to. If absent, the message goes to the channel selected for the I(api_key).
|
||||
username:
|
||||
description:
|
||||
- This is the sender of the message (Username Override need to be enabled by mattermost admin, see mattermost doc.
|
||||
default: Ansible
|
||||
icon_url:
|
||||
description:
|
||||
- Url for the message sender's icon.
|
||||
default: https://www.ansible.com/favicon.ico
|
||||
validate_certs:
|
||||
description:
|
||||
- If C(no), SSL certificates will not be validated. This should only be used
|
||||
on personally controlled sites using self-signed certificates.
|
||||
default: yes
|
||||
type: bool
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Send notification message via Mattermost
|
||||
mattermost:
|
||||
url: http://mattermost.example.com
|
||||
api_key: my_api_key
|
||||
text: '{{ inventory_hostname }} completed'
|
||||
|
||||
- name: Send notification message via Mattermost all options
|
||||
mattermost:
|
||||
url: http://mattermost.example.com
|
||||
api_key: my_api_key
|
||||
text: '{{ inventory_hostname }} completed'
|
||||
channel: notifications
|
||||
username: 'Ansible on {{ inventory_hostname }}'
|
||||
icon_url: http://www.example.com/some-image-file.png
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
payload:
|
||||
description: Mattermost payload
|
||||
returned: success
|
||||
type: str
|
||||
webhook_url:
|
||||
description: URL the webhook is sent to
|
||||
returned: success
|
||||
type: str
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
supports_check_mode=True,
|
||||
argument_spec=dict(
|
||||
url=dict(type='str', required=True),
|
||||
api_key=dict(type='str', required=True, no_log=True),
|
||||
text=dict(type='str', required=True),
|
||||
channel=dict(type='str', default=None),
|
||||
username=dict(type='str', default='Ansible'),
|
||||
icon_url=dict(type='str', default='https://www.ansible.com/favicon.ico'),
|
||||
validate_certs=dict(default='yes', type='bool'),
|
||||
)
|
||||
)
|
||||
# init return dict
|
||||
result = dict(changed=False, msg="OK")
|
||||
|
||||
# define webhook
|
||||
webhook_url = "{0}/hooks/{1}".format(module.params['url'], module.params['api_key'])
|
||||
result['webhook_url'] = webhook_url
|
||||
|
||||
# define payload
|
||||
payload = {}
|
||||
for param in ['text', 'channel', 'username', 'icon_url']:
|
||||
if module.params[param] is not None:
|
||||
payload[param] = module.params[param]
|
||||
|
||||
payload = module.jsonify(payload)
|
||||
result['payload'] = payload
|
||||
|
||||
# http headers
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
|
||||
# notes:
|
||||
# Nothing is done in check mode
|
||||
# it'll pass even if your server is down or/and if your token is invalid.
|
||||
# If someone find good way to check...
|
||||
|
||||
# send request if not in test mode
|
||||
if module.check_mode is False:
|
||||
response, info = fetch_url(module=module, url=webhook_url, headers=headers, method='POST', data=payload)
|
||||
|
||||
# something's wrong
|
||||
if info['status'] != 200:
|
||||
# some problem
|
||||
result['msg'] = "Failed to send mattermost message, the error was: {0}".format(info['msg'])
|
||||
module.fail_json(**result)
|
||||
|
||||
# Looks good
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
241
plugins/modules/notification/mqtt.py
Normal file
241
plugins/modules/notification/mqtt.py
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, 2014, Jan-Piet Mens <jpmens () gmail.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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: mqtt
|
||||
short_description: Publish a message on an MQTT topic for the IoT
|
||||
description:
|
||||
- Publish a message on an MQTT topic.
|
||||
options:
|
||||
server:
|
||||
description:
|
||||
- MQTT broker address/name
|
||||
default: localhost
|
||||
port:
|
||||
description:
|
||||
- MQTT broker port number
|
||||
default: 1883
|
||||
username:
|
||||
description:
|
||||
- Username to authenticate against the broker.
|
||||
password:
|
||||
description:
|
||||
- Password for C(username) to authenticate against the broker.
|
||||
client_id:
|
||||
description:
|
||||
- MQTT client identifier
|
||||
default: hostname + pid
|
||||
topic:
|
||||
description:
|
||||
- MQTT topic name
|
||||
required: true
|
||||
payload:
|
||||
description:
|
||||
- Payload. The special string C("None") may be used to send a NULL
|
||||
(i.e. empty) payload which is useful to simply notify with the I(topic)
|
||||
or to clear previously retained messages.
|
||||
required: true
|
||||
qos:
|
||||
description:
|
||||
- QoS (Quality of Service)
|
||||
default: 0
|
||||
choices: [ "0", "1", "2" ]
|
||||
retain:
|
||||
description:
|
||||
- Setting this flag causes the broker to retain (i.e. keep) the message so that
|
||||
applications that subsequently subscribe to the topic can received the last
|
||||
retained message immediately.
|
||||
type: bool
|
||||
default: 'no'
|
||||
ca_cert:
|
||||
description:
|
||||
- The path to the Certificate Authority certificate files that are to be
|
||||
treated as trusted by this client. If this is the only option given
|
||||
then the client will operate in a similar manner to a web browser. That
|
||||
is to say it will require the broker to have a certificate signed by the
|
||||
Certificate Authorities in ca_certs and will communicate using TLS v1,
|
||||
but will not attempt any form of authentication. This provides basic
|
||||
network encryption but may not be sufficient depending on how the broker
|
||||
is configured.
|
||||
aliases: [ ca_certs ]
|
||||
client_cert:
|
||||
description:
|
||||
- The path pointing to the PEM encoded client certificate. If this is not
|
||||
None it will be used as client information for TLS based
|
||||
authentication. Support for this feature is broker dependent.
|
||||
aliases: [ certfile ]
|
||||
client_key:
|
||||
description:
|
||||
- The path pointing to the PEM encoded client private key. If this is not
|
||||
None it will be used as client information for TLS based
|
||||
authentication. Support for this feature is broker dependent.
|
||||
aliases: [ keyfile ]
|
||||
tls_version:
|
||||
description:
|
||||
- Specifies the version of the SSL/TLS protocol to be used.
|
||||
- By default (if the python version supports it) the highest TLS version is
|
||||
detected. If unavailable, TLS v1 is used.
|
||||
type: str
|
||||
choices:
|
||||
- tlsv1.1
|
||||
- tlsv1.2
|
||||
requirements: [ mosquitto ]
|
||||
notes:
|
||||
- This module requires a connection to an MQTT broker such as Mosquitto
|
||||
U(http://mosquitto.org) and the I(Paho) C(mqtt) Python client (U(https://pypi.org/project/paho-mqtt/)).
|
||||
author: "Jan-Piet Mens (@jpmens)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- mqtt:
|
||||
topic: 'service/ansible/{{ ansible_hostname }}'
|
||||
payload: 'Hello at {{ ansible_date_time.iso8601 }}'
|
||||
qos: 0
|
||||
retain: False
|
||||
client_id: ans001
|
||||
delegate_to: localhost
|
||||
'''
|
||||
|
||||
# ===========================================
|
||||
# MQTT module support methods.
|
||||
#
|
||||
|
||||
import os
|
||||
import ssl
|
||||
import traceback
|
||||
import platform
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
HAS_PAHOMQTT = True
|
||||
PAHOMQTT_IMP_ERR = None
|
||||
try:
|
||||
import socket
|
||||
import paho.mqtt.publish as mqtt
|
||||
except ImportError:
|
||||
PAHOMQTT_IMP_ERR = traceback.format_exc()
|
||||
HAS_PAHOMQTT = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Main
|
||||
#
|
||||
|
||||
def main():
|
||||
tls_map = {}
|
||||
|
||||
try:
|
||||
tls_map['tlsv1.2'] = ssl.PROTOCOL_TLSv1_2
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
tls_map['tlsv1.1'] = ssl.PROTOCOL_TLSv1_1
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
server=dict(default='localhost'),
|
||||
port=dict(default=1883, type='int'),
|
||||
topic=dict(required=True),
|
||||
payload=dict(required=True),
|
||||
client_id=dict(default=None),
|
||||
qos=dict(default="0", choices=["0", "1", "2"]),
|
||||
retain=dict(default=False, type='bool'),
|
||||
username=dict(default=None),
|
||||
password=dict(default=None, no_log=True),
|
||||
ca_cert=dict(default=None, type='path', aliases=['ca_certs']),
|
||||
client_cert=dict(default=None, type='path', aliases=['certfile']),
|
||||
client_key=dict(default=None, type='path', aliases=['keyfile']),
|
||||
tls_version=dict(default=None, choices=['tlsv1.1', 'tlsv1.2'])
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
if not HAS_PAHOMQTT:
|
||||
module.fail_json(msg=missing_required_lib('paho-mqtt'), exception=PAHOMQTT_IMP_ERR)
|
||||
|
||||
server = module.params.get("server", 'localhost')
|
||||
port = module.params.get("port", 1883)
|
||||
topic = module.params.get("topic")
|
||||
payload = module.params.get("payload")
|
||||
client_id = module.params.get("client_id", '')
|
||||
qos = int(module.params.get("qos", 0))
|
||||
retain = module.params.get("retain")
|
||||
username = module.params.get("username", None)
|
||||
password = module.params.get("password", None)
|
||||
ca_certs = module.params.get("ca_cert", None)
|
||||
certfile = module.params.get("client_cert", None)
|
||||
keyfile = module.params.get("client_key", None)
|
||||
tls_version = module.params.get("tls_version", None)
|
||||
|
||||
if client_id is None:
|
||||
client_id = "%s_%s" % (socket.getfqdn(), os.getpid())
|
||||
|
||||
if payload and payload == 'None':
|
||||
payload = None
|
||||
|
||||
auth = None
|
||||
if username is not None:
|
||||
auth = {'username': username, 'password': password}
|
||||
|
||||
tls = None
|
||||
if ca_certs is not None:
|
||||
if tls_version:
|
||||
tls_version = tls_map.get(tls_version, ssl.PROTOCOL_SSLv23)
|
||||
else:
|
||||
if LooseVersion(platform.python_version()) <= "3.5.2":
|
||||
# Specifying `None` on later versions of python seems sufficient to
|
||||
# instruct python to autonegotiate the SSL/TLS connection. On versions
|
||||
# 3.5.2 and lower though we need to specify the version.
|
||||
#
|
||||
# Note that this is an alias for PROTOCOL_TLS, but PROTOCOL_TLS was
|
||||
# not available until 3.5.3.
|
||||
tls_version = ssl.PROTOCOL_SSLv23
|
||||
|
||||
tls = {
|
||||
'ca_certs': ca_certs,
|
||||
'certfile': certfile,
|
||||
'keyfile': keyfile,
|
||||
'tls_version': tls_version,
|
||||
}
|
||||
|
||||
try:
|
||||
mqtt.single(
|
||||
topic,
|
||||
payload,
|
||||
qos=qos,
|
||||
retain=retain,
|
||||
client_id=client_id,
|
||||
hostname=server,
|
||||
port=port,
|
||||
auth=auth,
|
||||
tls=tls
|
||||
)
|
||||
except Exception as e:
|
||||
module.fail_json(
|
||||
msg="unable to publish to MQTT broker %s" % to_native(e),
|
||||
exception=traceback.format_exc()
|
||||
)
|
||||
|
||||
module.exit_json(changed=False, topic=topic)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
135
plugins/modules/notification/nexmo.py
Normal file
135
plugins/modules/notification/nexmo.py
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2014, Matt Martz <matt@sivel.net>
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: nexmo
|
||||
short_description: Send a SMS via nexmo
|
||||
description:
|
||||
- Send a SMS message via nexmo
|
||||
author: "Matt Martz (@sivel)"
|
||||
options:
|
||||
api_key:
|
||||
description:
|
||||
- Nexmo API Key
|
||||
required: true
|
||||
api_secret:
|
||||
description:
|
||||
- Nexmo API Secret
|
||||
required: true
|
||||
src:
|
||||
description:
|
||||
- Nexmo Number to send from
|
||||
required: true
|
||||
dest:
|
||||
description:
|
||||
- Phone number(s) to send SMS message to
|
||||
required: true
|
||||
msg:
|
||||
description:
|
||||
- Message to text to send. Messages longer than 160 characters will be
|
||||
split into multiple messages
|
||||
required: true
|
||||
validate_certs:
|
||||
description:
|
||||
- If C(no), SSL certificates will not be validated. This should only be used
|
||||
on personally controlled sites using self-signed certificates.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
extends_documentation_fragment:
|
||||
- url
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Send notification message via Nexmo
|
||||
nexmo:
|
||||
api_key: 640c8a53
|
||||
api_secret: 0ce239a6
|
||||
src: 12345678901
|
||||
dest:
|
||||
- 10987654321
|
||||
- 16789012345
|
||||
msg: '{{ inventory_hostname }} completed'
|
||||
delegate_to: localhost
|
||||
"""
|
||||
import json
|
||||
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url, url_argument_spec
|
||||
|
||||
|
||||
NEXMO_API = 'https://rest.nexmo.com/sms/json'
|
||||
|
||||
|
||||
def send_msg(module):
|
||||
failed = list()
|
||||
responses = dict()
|
||||
msg = {
|
||||
'api_key': module.params.get('api_key'),
|
||||
'api_secret': module.params.get('api_secret'),
|
||||
'from': module.params.get('src'),
|
||||
'text': module.params.get('msg')
|
||||
}
|
||||
for number in module.params.get('dest'):
|
||||
msg['to'] = number
|
||||
url = "%s?%s" % (NEXMO_API, urlencode(msg))
|
||||
|
||||
headers = dict(Accept='application/json')
|
||||
response, info = fetch_url(module, url, headers=headers)
|
||||
if info['status'] != 200:
|
||||
failed.append(number)
|
||||
responses[number] = dict(failed=True)
|
||||
|
||||
try:
|
||||
responses[number] = json.load(response)
|
||||
except Exception:
|
||||
failed.append(number)
|
||||
responses[number] = dict(failed=True)
|
||||
else:
|
||||
for message in responses[number]['messages']:
|
||||
if int(message['status']) != 0:
|
||||
failed.append(number)
|
||||
responses[number] = dict(failed=True, **responses[number])
|
||||
|
||||
if failed:
|
||||
msg = 'One or messages failed to send'
|
||||
else:
|
||||
msg = ''
|
||||
|
||||
module.exit_json(failed=bool(failed), msg=msg, changed=False,
|
||||
responses=responses)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = url_argument_spec()
|
||||
argument_spec.update(
|
||||
dict(
|
||||
api_key=dict(required=True, no_log=True),
|
||||
api_secret=dict(required=True, no_log=True),
|
||||
src=dict(required=True, type='int'),
|
||||
dest=dict(required=True, type='list'),
|
||||
msg=dict(required=True),
|
||||
),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec
|
||||
)
|
||||
|
||||
send_msg(module)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
296
plugins/modules/notification/office_365_connector_card.py
Normal file
296
plugins/modules/notification/office_365_connector_card.py
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017 Marc Sensenich <hello@marc-sensenich.com>
|
||||
# Copyright (c) 2017 Ansible Project
|
||||
# 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
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: office_365_connector_card
|
||||
short_description: Use webhooks to create Connector Card messages within an Office 365 group
|
||||
description:
|
||||
- Creates Connector Card messages through
|
||||
- Office 365 Connectors U(https://dev.outlook.com/Connectors)
|
||||
author: "Marc Sensenich (@marc-sensenich)"
|
||||
notes:
|
||||
- This module is not idempotent, therefore if the same task is run twice
|
||||
there will be two Connector Cards created
|
||||
options:
|
||||
webhook:
|
||||
description:
|
||||
- The webhook URL is given to you when you create a new Connector.
|
||||
required: true
|
||||
summary:
|
||||
description:
|
||||
- A string used for summarizing card content.
|
||||
- This will be shown as the message subject.
|
||||
- This is required if the text parameter isn't populated.
|
||||
color:
|
||||
description:
|
||||
- Accent color used for branding or indicating status in the card.
|
||||
title:
|
||||
description:
|
||||
- A title for the Connector message. Shown at the top of the message.
|
||||
text:
|
||||
description:
|
||||
- The main text of the card.
|
||||
- This will be rendered below the sender information and optional title,
|
||||
- and above any sections or actions present.
|
||||
actions:
|
||||
description:
|
||||
- This array of objects will power the action links
|
||||
- found at the bottom of the card.
|
||||
sections:
|
||||
description:
|
||||
- Contains a list of sections to display in the card.
|
||||
- For more information see https://dev.outlook.com/Connectors/reference.
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Create a simple Connector Card
|
||||
office_365_connector_card:
|
||||
webhook: https://outlook.office.com/webhook/GUID/IncomingWebhook/GUID/GUID
|
||||
text: 'Hello, World!'
|
||||
|
||||
- name: Create a Connector Card with the full format
|
||||
office_365_connector_card:
|
||||
webhook: https://outlook.office.com/webhook/GUID/IncomingWebhook/GUID/GUID
|
||||
summary: This is the summary property
|
||||
title: This is the **card's title** property
|
||||
text: This is the **card's text** property. Lorem ipsum dolor sit amet, consectetur
|
||||
adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
color: E81123
|
||||
sections:
|
||||
- title: This is the **section's title** property
|
||||
activity_image: http://connectorsdemo.azurewebsites.net/images/MSC12_Oscar_002.jpg
|
||||
activity_title: This is the section's **activityTitle** property
|
||||
activity_subtitle: This is the section's **activitySubtitle** property
|
||||
activity_text: This is the section's **activityText** property.
|
||||
hero_image:
|
||||
image: http://connectorsdemo.azurewebsites.net/images/WIN12_Scene_01.jpg
|
||||
title: This is the image's alternate text
|
||||
text: This is the section's text property. Lorem ipsum dolor sit amet, consectetur
|
||||
adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
facts:
|
||||
- name: This is a fact name
|
||||
value: This is a fact value
|
||||
- name: This is a fact name
|
||||
value: This is a fact value
|
||||
- name: This is a fact name
|
||||
value: This is a fact value
|
||||
images:
|
||||
- image: http://connectorsdemo.azurewebsites.net/images/MicrosoftSurface_024_Cafe_OH-06315_VS_R1c.jpg
|
||||
title: This is the image's alternate text
|
||||
- image: http://connectorsdemo.azurewebsites.net/images/WIN12_Scene_01.jpg
|
||||
title: This is the image's alternate text
|
||||
- image: http://connectorsdemo.azurewebsites.net/images/WIN12_Anthony_02.jpg
|
||||
title: This is the image's alternate text
|
||||
actions:
|
||||
- "@type": ActionCard
|
||||
name: Comment
|
||||
inputs:
|
||||
- "@type": TextInput
|
||||
id: comment
|
||||
is_multiline: true
|
||||
title: Input's title property
|
||||
actions:
|
||||
- "@type": HttpPOST
|
||||
name: Save
|
||||
target: http://...
|
||||
- "@type": ActionCard
|
||||
name: Due Date
|
||||
inputs:
|
||||
- "@type": DateInput
|
||||
id: dueDate
|
||||
title: Input's title property
|
||||
actions:
|
||||
- "@type": HttpPOST
|
||||
name: Save
|
||||
target: http://...
|
||||
- "@type": HttpPOST
|
||||
name: Action's name prop.
|
||||
target: http://...
|
||||
- "@type": OpenUri
|
||||
name: Action's name prop
|
||||
targets:
|
||||
- os: default
|
||||
uri: http://...
|
||||
- start_group: true
|
||||
title: This is the title of a **second section**
|
||||
text: This second section is visually separated from the first one by setting its
|
||||
**startGroup** property to true.
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict
|
||||
|
||||
OFFICE_365_CARD_CONTEXT = "http://schema.org/extensions"
|
||||
OFFICE_365_CARD_TYPE = "MessageCard"
|
||||
OFFICE_365_CARD_EMPTY_PAYLOAD_MSG = "Summary or Text is required."
|
||||
OFFICE_365_INVALID_WEBHOOK_MSG = "The Incoming Webhook was not reachable."
|
||||
|
||||
|
||||
def build_actions(actions):
|
||||
action_items = []
|
||||
|
||||
for action in actions:
|
||||
action_item = snake_dict_to_camel_dict(action)
|
||||
action_items.append(action_item)
|
||||
|
||||
return action_items
|
||||
|
||||
|
||||
def build_sections(sections):
|
||||
sections_created = []
|
||||
|
||||
for section in sections:
|
||||
sections_created.append(build_section(section))
|
||||
|
||||
return sections_created
|
||||
|
||||
|
||||
def build_section(section):
|
||||
section_payload = dict()
|
||||
|
||||
if 'title' in section:
|
||||
section_payload['title'] = section['title']
|
||||
|
||||
if 'start_group' in section:
|
||||
section_payload['startGroup'] = section['start_group']
|
||||
|
||||
if 'activity_image' in section:
|
||||
section_payload['activityImage'] = section['activity_image']
|
||||
|
||||
if 'activity_title' in section:
|
||||
section_payload['activityTitle'] = section['activity_title']
|
||||
|
||||
if 'activity_subtitle' in section:
|
||||
section_payload['activitySubtitle'] = section['activity_subtitle']
|
||||
|
||||
if 'activity_text' in section:
|
||||
section_payload['activityText'] = section['activity_text']
|
||||
|
||||
if 'hero_image' in section:
|
||||
section_payload['heroImage'] = section['hero_image']
|
||||
|
||||
if 'text' in section:
|
||||
section_payload['text'] = section['text']
|
||||
|
||||
if 'facts' in section:
|
||||
section_payload['facts'] = section['facts']
|
||||
|
||||
if 'images' in section:
|
||||
section_payload['images'] = section['images']
|
||||
|
||||
if 'actions' in section:
|
||||
section_payload['potentialAction'] = build_actions(section['actions'])
|
||||
|
||||
return section_payload
|
||||
|
||||
|
||||
def build_payload_for_connector_card(module, summary=None, color=None, title=None, text=None, actions=None, sections=None):
|
||||
payload = dict()
|
||||
payload['@context'] = OFFICE_365_CARD_CONTEXT
|
||||
payload['@type'] = OFFICE_365_CARD_TYPE
|
||||
|
||||
if summary is not None:
|
||||
payload['summary'] = summary
|
||||
|
||||
if color is not None:
|
||||
payload['themeColor'] = color
|
||||
|
||||
if title is not None:
|
||||
payload['title'] = title
|
||||
|
||||
if text is not None:
|
||||
payload['text'] = text
|
||||
|
||||
if actions:
|
||||
payload['potentialAction'] = build_actions(actions)
|
||||
|
||||
if sections:
|
||||
payload['sections'] = build_sections(sections)
|
||||
|
||||
payload = module.jsonify(payload)
|
||||
return payload
|
||||
|
||||
|
||||
def do_notify_connector_card_webhook(module, webhook, payload):
|
||||
headers = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
response, info = fetch_url(
|
||||
module=module,
|
||||
url=webhook,
|
||||
headers=headers,
|
||||
method='POST',
|
||||
data=payload
|
||||
)
|
||||
|
||||
if info['status'] == 200:
|
||||
module.exit_json(changed=True)
|
||||
elif info['status'] == 400 and module.check_mode:
|
||||
if info['body'] == OFFICE_365_CARD_EMPTY_PAYLOAD_MSG:
|
||||
module.exit_json(changed=True)
|
||||
else:
|
||||
module.fail_json(msg=OFFICE_365_INVALID_WEBHOOK_MSG)
|
||||
else:
|
||||
module.fail_json(
|
||||
msg="failed to send %s as a connector card to Incoming Webhook: %s"
|
||||
% (payload, info['msg'])
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
webhook=dict(required=True, no_log=True),
|
||||
summary=dict(type='str'),
|
||||
color=dict(type='str'),
|
||||
title=dict(type='str'),
|
||||
text=dict(type='str'),
|
||||
actions=dict(type='list'),
|
||||
sections=dict(type='list')
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
webhook = module.params['webhook']
|
||||
summary = module.params['summary']
|
||||
color = module.params['color']
|
||||
title = module.params['title']
|
||||
text = module.params['text']
|
||||
actions = module.params['actions']
|
||||
sections = module.params['sections']
|
||||
|
||||
payload = build_payload_for_connector_card(
|
||||
module,
|
||||
summary,
|
||||
color,
|
||||
title,
|
||||
text,
|
||||
actions,
|
||||
sections)
|
||||
|
||||
if module.check_mode:
|
||||
# In check mode, send an empty payload to validate connection
|
||||
check_mode_payload = build_payload_for_connector_card(module)
|
||||
do_notify_connector_card_webhook(module, webhook, check_mode_payload)
|
||||
|
||||
do_notify_connector_card_webhook(module, webhook, payload)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
1
plugins/modules/notification/osx_say.py
Symbolic link
1
plugins/modules/notification/osx_say.py
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
say.py
|
||||
184
plugins/modules/notification/pushbullet.py
Normal file
184
plugins/modules/notification/pushbullet.py
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
author: "Willy Barro (@willybarro)"
|
||||
requirements: [ pushbullet.py ]
|
||||
module: pushbullet
|
||||
short_description: Sends notifications to Pushbullet
|
||||
description:
|
||||
- This module sends push notifications via Pushbullet to channels or devices.
|
||||
options:
|
||||
api_key:
|
||||
description:
|
||||
- Push bullet API token
|
||||
required: true
|
||||
channel:
|
||||
description:
|
||||
- The channel TAG you wish to broadcast a push notification,
|
||||
as seen on the "My Channels" > "Edit your channel" at
|
||||
Pushbullet page.
|
||||
device:
|
||||
description:
|
||||
- The device NAME you wish to send a push notification,
|
||||
as seen on the Pushbullet main page.
|
||||
push_type:
|
||||
description:
|
||||
- Thing you wish to push.
|
||||
default: note
|
||||
choices: [ "note", "link" ]
|
||||
title:
|
||||
description:
|
||||
- Title of the notification.
|
||||
required: true
|
||||
body:
|
||||
description:
|
||||
- Body of the notification, e.g. Details of the fault you're alerting.
|
||||
|
||||
notes:
|
||||
- Requires pushbullet.py Python package on the remote host.
|
||||
You can install it via pip with ($ pip install pushbullet.py).
|
||||
See U(https://github.com/randomchars/pushbullet.py)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Sends a push notification to a device
|
||||
- pushbullet:
|
||||
api_key: "ABC123abc123ABC123abc123ABC123ab"
|
||||
device: "Chrome"
|
||||
title: "You may see this on Google Chrome"
|
||||
|
||||
# Sends a link to a device
|
||||
- pushbullet:
|
||||
api_key: ABC123abc123ABC123abc123ABC123ab
|
||||
device: Chrome
|
||||
push_type: link
|
||||
title: Ansible Documentation
|
||||
body: https://docs.ansible.com/
|
||||
|
||||
# Sends a push notification to a channel
|
||||
- pushbullet:
|
||||
api_key: ABC123abc123ABC123abc123ABC123ab
|
||||
channel: my-awesome-channel
|
||||
title: Broadcasting a message to the #my-awesome-channel folks
|
||||
|
||||
# Sends a push notification with title and body to a channel
|
||||
- pushbullet:
|
||||
api_key: ABC123abc123ABC123abc123ABC123ab
|
||||
channel: my-awesome-channel
|
||||
title: ALERT! Signup service is down
|
||||
body: Error rate on signup service is over 90% for more than 2 minutes
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
PUSHBULLET_IMP_ERR = None
|
||||
try:
|
||||
from pushbullet import PushBullet
|
||||
from pushbullet.errors import InvalidKeyError, PushError
|
||||
except ImportError:
|
||||
PUSHBULLET_IMP_ERR = traceback.format_exc()
|
||||
pushbullet_found = False
|
||||
else:
|
||||
pushbullet_found = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Main
|
||||
#
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
api_key=dict(type='str', required=True, no_log=True),
|
||||
channel=dict(type='str', default=None),
|
||||
device=dict(type='str', default=None),
|
||||
push_type=dict(type='str', default="note", choices=['note', 'link']),
|
||||
title=dict(type='str', required=True),
|
||||
body=dict(type='str', default=None),
|
||||
url=dict(type='str', default=None),
|
||||
),
|
||||
mutually_exclusive=(
|
||||
['channel', 'device'],
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
api_key = module.params['api_key']
|
||||
channel = module.params['channel']
|
||||
device = module.params['device']
|
||||
push_type = module.params['push_type']
|
||||
title = module.params['title']
|
||||
body = module.params['body']
|
||||
url = module.params['url']
|
||||
|
||||
if not pushbullet_found:
|
||||
module.fail_json(msg=missing_required_lib('pushbullet.py'), exception=PUSHBULLET_IMP_ERR)
|
||||
|
||||
# Init pushbullet
|
||||
try:
|
||||
pb = PushBullet(api_key)
|
||||
target = None
|
||||
except InvalidKeyError:
|
||||
module.fail_json(msg="Invalid api_key")
|
||||
|
||||
# Checks for channel/device
|
||||
if device is None and channel is None:
|
||||
module.fail_json(msg="You need to provide a channel or a device.")
|
||||
|
||||
# Search for given device
|
||||
if device is not None:
|
||||
devices_by_nickname = {}
|
||||
for d in pb.devices:
|
||||
devices_by_nickname[d.nickname] = d
|
||||
|
||||
if device in devices_by_nickname:
|
||||
target = devices_by_nickname[device]
|
||||
else:
|
||||
module.fail_json(msg="Device '%s' not found. Available devices: '%s'" % (device, "', '".join(devices_by_nickname.keys())))
|
||||
|
||||
# Search for given channel
|
||||
if channel is not None:
|
||||
channels_by_tag = {}
|
||||
for c in pb.channels:
|
||||
channels_by_tag[c.channel_tag] = c
|
||||
|
||||
if channel in channels_by_tag:
|
||||
target = channels_by_tag[channel]
|
||||
else:
|
||||
module.fail_json(msg="Channel '%s' not found. Available channels: '%s'" % (channel, "', '".join(channels_by_tag.keys())))
|
||||
|
||||
# If in check mode, exit saying that we succeeded
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=False, msg="OK")
|
||||
|
||||
# Send push notification
|
||||
try:
|
||||
if push_type == "link":
|
||||
target.push_link(title, url, body)
|
||||
else:
|
||||
target.push_note(title, body)
|
||||
module.exit_json(changed=False, msg="OK")
|
||||
except PushError as e:
|
||||
module.fail_json(msg="An error occurred, Pushbullet's response: %s" % str(e))
|
||||
|
||||
module.fail_json(msg="An unknown error has occurred")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
127
plugins/modules/notification/pushover.py
Normal file
127
plugins/modules/notification/pushover.py
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2012, Jim Richardson <weaselkeeper@gmail.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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: pushover
|
||||
short_description: Send notifications via U(https://pushover.net)
|
||||
description:
|
||||
- Send notifications via pushover, to subscriber list of devices, and email
|
||||
addresses. Requires pushover app on devices.
|
||||
notes:
|
||||
- You will require a pushover.net account to use this module. But no account
|
||||
is required to receive messages.
|
||||
options:
|
||||
msg:
|
||||
description:
|
||||
- What message you wish to send.
|
||||
required: true
|
||||
app_token:
|
||||
description:
|
||||
- Pushover issued token identifying your pushover app.
|
||||
required: true
|
||||
user_key:
|
||||
description:
|
||||
- Pushover issued authentication key for your user.
|
||||
required: true
|
||||
title:
|
||||
description:
|
||||
- Message title.
|
||||
required: false
|
||||
pri:
|
||||
description:
|
||||
- Message priority (see U(https://pushover.net) for details).
|
||||
required: false
|
||||
|
||||
author: "Jim Richardson (@weaselkeeper)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- pushover:
|
||||
msg: '{{ inventory_hostname }} is acting strange ...'
|
||||
app_token: wxfdksl
|
||||
user_key: baa5fe97f2c5ab3ca8f0bb59
|
||||
delegate_to: localhost
|
||||
|
||||
- pushover:
|
||||
title: 'Alert!'
|
||||
msg: '{{ inventory_hostname }} has exploded in flames, It is now time to panic'
|
||||
pri: 1
|
||||
app_token: wxfdksl
|
||||
user_key: baa5fe97f2c5ab3ca8f0bb59
|
||||
delegate_to: localhost
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
class Pushover(object):
|
||||
''' Instantiates a pushover object, use it to send notifications '''
|
||||
base_uri = 'https://api.pushover.net'
|
||||
|
||||
def __init__(self, module, user, token):
|
||||
self.module = module
|
||||
self.user = user
|
||||
self.token = token
|
||||
|
||||
def run(self, priority, msg, title):
|
||||
''' Do, whatever it is, we do. '''
|
||||
|
||||
url = '%s/1/messages.json' % (self.base_uri)
|
||||
|
||||
# parse config
|
||||
options = dict(user=self.user,
|
||||
token=self.token,
|
||||
priority=priority,
|
||||
message=msg)
|
||||
|
||||
if title is not None:
|
||||
options = dict(options,
|
||||
title=title)
|
||||
|
||||
data = urlencode(options)
|
||||
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded"}
|
||||
r, info = fetch_url(self.module, url, method='POST', data=data, headers=headers)
|
||||
if info['status'] != 200:
|
||||
raise Exception(info)
|
||||
|
||||
return r.read()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
title=dict(type='str'),
|
||||
msg=dict(required=True),
|
||||
app_token=dict(required=True, no_log=True),
|
||||
user_key=dict(required=True, no_log=True),
|
||||
pri=dict(required=False, default='0', choices=['-2', '-1', '0', '1', '2']),
|
||||
),
|
||||
)
|
||||
|
||||
msg_object = Pushover(module, module.params['user_key'], module.params['app_token'])
|
||||
try:
|
||||
response = msg_object.run(module.params['pri'], module.params['msg'], module.params['title'])
|
||||
except Exception:
|
||||
module.fail_json(msg='Unable to send msg via pushover')
|
||||
|
||||
module.exit_json(msg='message sent successfully: %s' % response, changed=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
209
plugins/modules/notification/rabbitmq_publish.py
Normal file
209
plugins/modules/notification/rabbitmq_publish.py
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2018, John Imison <john+github@imison.net>
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: rabbitmq_publish
|
||||
short_description: Publish a message to a RabbitMQ queue.
|
||||
description:
|
||||
- Publish a message on a RabbitMQ queue using a blocking connection.
|
||||
options:
|
||||
url:
|
||||
description:
|
||||
- An URL connection string to connect to the RabbitMQ server.
|
||||
- I(url) and I(host)/I(port)/I(user)/I(pass)/I(vhost) are mutually exclusive, use either or but not both.
|
||||
proto:
|
||||
description:
|
||||
- The protocol to use.
|
||||
choices: [amqps, amqp]
|
||||
host:
|
||||
description:
|
||||
- The RabbitMQ server hostname or IP.
|
||||
port:
|
||||
description:
|
||||
- The RabbitMQ server port.
|
||||
username:
|
||||
description:
|
||||
- The RabbitMQ username.
|
||||
password:
|
||||
description:
|
||||
- The RabbitMQ password.
|
||||
vhost:
|
||||
description:
|
||||
- The virtual host to target.
|
||||
- If default vhost is required, use C('%2F').
|
||||
queue:
|
||||
description:
|
||||
- The queue to publish a message to. If no queue is specified, RabbitMQ will return a random queue name.
|
||||
exchange:
|
||||
description:
|
||||
- The exchange to publish a message to.
|
||||
routing_key:
|
||||
description:
|
||||
- The routing key.
|
||||
body:
|
||||
description:
|
||||
- The body of the message.
|
||||
- A C(body) cannot be provided if a C(src) is specified.
|
||||
src:
|
||||
description:
|
||||
- A file to upload to the queue. Automatic mime type detection is attempted if content_type is not defined (left as default).
|
||||
- A C(src) cannot be provided if a C(body) is specified.
|
||||
- The filename is added to the headers of the posted message to RabbitMQ. Key being the C(filename), value is the filename.
|
||||
aliases: ['file']
|
||||
content_type:
|
||||
description:
|
||||
- The content type of the body.
|
||||
default: text/plain
|
||||
durable:
|
||||
description:
|
||||
- Set the queue to be durable.
|
||||
default: False
|
||||
type: bool
|
||||
exclusive:
|
||||
description:
|
||||
- Set the queue to be exclusive.
|
||||
default: False
|
||||
type: bool
|
||||
auto_delete:
|
||||
description:
|
||||
- Set the queue to auto delete.
|
||||
default: False
|
||||
type: bool
|
||||
headers:
|
||||
description:
|
||||
- A dictionary of headers to post with the message.
|
||||
default: {}
|
||||
type: dict
|
||||
cafile:
|
||||
description:
|
||||
- CA file used during connection to the RabbitMQ server over SSL.
|
||||
- If this option is specified, also I(certfile) and I(keyfile) must be specified.
|
||||
certfile:
|
||||
description:
|
||||
- Client certificate to establish SSL connection.
|
||||
- If this option is specified, also I(cafile) and I(keyfile) must be specified.
|
||||
keyfile:
|
||||
description:
|
||||
- Client key to establish SSL connection.
|
||||
- If this option is specified, also I(cafile) and I(certfile) must be specified.
|
||||
|
||||
|
||||
|
||||
requirements: [ pika ]
|
||||
notes:
|
||||
- This module requires the pika python library U(https://pika.readthedocs.io/).
|
||||
- Pika is a pure-Python implementation of the AMQP 0-9-1 protocol that tries to stay fairly independent of the underlying network support library.
|
||||
- This module is tested against RabbitMQ. Other AMQP 0.9.1 protocol based servers may work but not tested/guaranteed.
|
||||
- The certificate authentication was tested with certificates created
|
||||
via U(https://www.rabbitmq.com/ssl.html#automated-certificate-generation) and RabbitMQ
|
||||
configuration variables C(ssl_options.verify = verify_peer) & C(ssl_options.fail_if_no_peer_cert = true).
|
||||
author: "John Imison (@Im0)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Publish a message to a queue with headers
|
||||
rabbitmq_publish:
|
||||
url: "amqp://guest:guest@192.168.0.32:5672/%2F"
|
||||
queue: 'test'
|
||||
body: "Hello world from ansible module rabbitmq_publish"
|
||||
content_type: "text/plain"
|
||||
headers:
|
||||
myHeader: myHeaderValue
|
||||
|
||||
|
||||
- name: Publish a file to a queue
|
||||
rabbitmq_publish:
|
||||
url: "amqp://guest:guest@192.168.0.32:5672/%2F"
|
||||
queue: 'images'
|
||||
file: 'path/to/logo.gif'
|
||||
|
||||
- name: RabbitMQ auto generated queue
|
||||
rabbitmq_publish:
|
||||
url: "amqp://guest:guest@192.168.0.32:5672/%2F"
|
||||
body: "Hello world random queue from ansible module rabbitmq_publish"
|
||||
content_type: "text/plain"
|
||||
|
||||
- name: Publish with certs
|
||||
rabbitmq_publish:
|
||||
url: "amqps://guest:guest@192.168.0.32:5671/%2F"
|
||||
body: "Hello test queue from ansible module rabbitmq_publish via SSL certs"
|
||||
queue: 'test'
|
||||
content_type: "text/plain"
|
||||
cafile: 'ca_certificate.pem'
|
||||
certfile: 'client_certificate.pem'
|
||||
keyfile: 'client_key.pem'
|
||||
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
result:
|
||||
description:
|
||||
- Contains the status I(msg), content type I(content_type) and the queue name I(queue).
|
||||
returned: success
|
||||
type: dict
|
||||
sample: |
|
||||
'result': { 'content_type': 'text/plain', 'msg': 'Successfully published to queue test', 'queue': 'test' }
|
||||
'''
|
||||
|
||||
try:
|
||||
import pika
|
||||
HAS_PIKA = True
|
||||
except ImportError:
|
||||
HAS_PIKA = False
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_native, to_text
|
||||
from ansible_collections.community.general.plugins.module_utils.rabbitmq import RabbitClient
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = RabbitClient.rabbitmq_argument_spec()
|
||||
argument_spec.update(
|
||||
exchange=dict(type='str', default=''),
|
||||
routing_key=dict(type='str', required=False),
|
||||
body=dict(type='str', required=False),
|
||||
src=dict(aliases=['file'], type='path', required=False),
|
||||
content_type=dict(default="text/plain", type='str'),
|
||||
durable=dict(default=False, type='bool'),
|
||||
exclusive=dict(default=False, type='bool'),
|
||||
auto_delete=dict(default=False, type='bool'),
|
||||
headers=dict(default={}, type='dict'),
|
||||
cafile=dict(type='str', required=False),
|
||||
certfile=dict(type='str', required=False),
|
||||
keyfile=dict(type='str', required=False),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=[['body', 'src']],
|
||||
required_together=[['cafile', 'certfile', 'keyfile']],
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
||||
rabbitmq = RabbitClient(module)
|
||||
|
||||
if rabbitmq.basic_publish():
|
||||
rabbitmq.close_connection()
|
||||
module.exit_json(changed=True, result={"msg": "Successfully published to queue %s" % rabbitmq.queue,
|
||||
"queue": rabbitmq.queue,
|
||||
"content_type": rabbitmq.content_type})
|
||||
else:
|
||||
rabbitmq.close_connection()
|
||||
module.fail_json(changed=False, msg="Unsuccessful publishing to queue %s" % rabbitmq.queue)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
235
plugins/modules/notification/rocketchat.py
Normal file
235
plugins/modules/notification/rocketchat.py
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2016, Deepak Kothandan <deepak.kothandan@outlook.com>
|
||||
# (c) 2015, Stefan Berggren <nsg@nsg.cc>
|
||||
# (c) 2014, Ramon de la Fuente <ramon@delafuente.nl>
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: rocketchat
|
||||
short_description: Send notifications to Rocket Chat
|
||||
description:
|
||||
- The C(rocketchat) module sends notifications to Rocket Chat via the Incoming WebHook integration
|
||||
author: "Ramon de la Fuente (@ramondelafuente)"
|
||||
options:
|
||||
domain:
|
||||
description:
|
||||
- The domain for your environment without protocol. (i.e.
|
||||
C(example.com) or C(chat.example.com))
|
||||
required: true
|
||||
token:
|
||||
description:
|
||||
- Rocket Chat Incoming Webhook integration token. This provides
|
||||
authentication to Rocket Chat's Incoming webhook for posting
|
||||
messages.
|
||||
required: true
|
||||
protocol:
|
||||
description:
|
||||
- Specify the protocol used to send notification messages before the webhook url. (i.e. http or https)
|
||||
default: https
|
||||
choices:
|
||||
- 'http'
|
||||
- 'https'
|
||||
msg:
|
||||
description:
|
||||
- Message to be sent.
|
||||
channel:
|
||||
description:
|
||||
- Channel to send the message to. If absent, the message goes to the channel selected for the I(token)
|
||||
specified during the creation of webhook.
|
||||
username:
|
||||
description:
|
||||
- This is the sender of the message.
|
||||
default: "Ansible"
|
||||
icon_url:
|
||||
description:
|
||||
- URL for the message sender's icon.
|
||||
default: "https://www.ansible.com/favicon.ico"
|
||||
icon_emoji:
|
||||
description:
|
||||
- Emoji for the message sender. The representation for the available emojis can be
|
||||
got from Rocket Chat. (for example :thumbsup:) (if I(icon_emoji) is set, I(icon_url) will not be used)
|
||||
link_names:
|
||||
description:
|
||||
- Automatically create links for channels and usernames in I(msg).
|
||||
default: 1
|
||||
choices:
|
||||
- 1
|
||||
- 0
|
||||
validate_certs:
|
||||
description:
|
||||
- If C(no), SSL certificates will not be validated. This should only be used
|
||||
on personally controlled sites using self-signed certificates.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
color:
|
||||
description:
|
||||
- Allow text to use default colors - use the default of 'normal' to not send a custom color bar at the start of the message
|
||||
default: 'normal'
|
||||
choices:
|
||||
- 'normal'
|
||||
- 'good'
|
||||
- 'warning'
|
||||
- 'danger'
|
||||
attachments:
|
||||
description:
|
||||
- Define a list of attachments.
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Send notification message via Rocket Chat
|
||||
rocketchat:
|
||||
token: thetoken/generatedby/rocketchat
|
||||
domain: chat.example.com
|
||||
msg: '{{ inventory_hostname }} completed'
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Send notification message via Rocket Chat all options
|
||||
rocketchat:
|
||||
domain: chat.example.com
|
||||
token: thetoken/generatedby/rocketchat
|
||||
msg: '{{ inventory_hostname }} completed'
|
||||
channel: #ansible
|
||||
username: 'Ansible on {{ inventory_hostname }}'
|
||||
icon_url: http://www.example.com/some-image-file.png
|
||||
link_names: 0
|
||||
delegate_to: localhost
|
||||
|
||||
- name: insert a color bar in front of the message for visibility purposes and use the default webhook icon and name configured in rocketchat
|
||||
rocketchat:
|
||||
token: thetoken/generatedby/rocketchat
|
||||
domain: chat.example.com
|
||||
msg: '{{ inventory_hostname }} is alive!'
|
||||
color: good
|
||||
username: ''
|
||||
icon_url: ''
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Use the attachments API
|
||||
rocketchat:
|
||||
token: thetoken/generatedby/rocketchat
|
||||
domain: chat.example.com
|
||||
attachments:
|
||||
- text: Display my system load on host A and B
|
||||
color: #ff00dd
|
||||
title: System load
|
||||
fields:
|
||||
- title: System A
|
||||
value: 'load average: 0,74, 0,66, 0,63'
|
||||
short: True
|
||||
- title: System B
|
||||
value: 'load average: 5,16, 4,64, 2,43'
|
||||
short: True
|
||||
delegate_to: localhost
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
changed:
|
||||
description: A flag indicating if any change was made or not.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: false
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
ROCKETCHAT_INCOMING_WEBHOOK = '%s://%s/hooks/%s'
|
||||
|
||||
|
||||
def build_payload_for_rocketchat(module, text, channel, username, icon_url, icon_emoji, link_names, color, attachments):
|
||||
payload = {}
|
||||
if color == "normal" and text is not None:
|
||||
payload = dict(text=text)
|
||||
elif text is not None:
|
||||
payload = dict(attachments=[dict(text=text, color=color)])
|
||||
if channel is not None:
|
||||
if (channel[0] == '#') or (channel[0] == '@'):
|
||||
payload['channel'] = channel
|
||||
else:
|
||||
payload['channel'] = '#' + channel
|
||||
if username is not None:
|
||||
payload['username'] = username
|
||||
if icon_emoji is not None:
|
||||
payload['icon_emoji'] = icon_emoji
|
||||
else:
|
||||
payload['icon_url'] = icon_url
|
||||
if link_names is not None:
|
||||
payload['link_names'] = link_names
|
||||
|
||||
if attachments is not None:
|
||||
if 'attachments' not in payload:
|
||||
payload['attachments'] = []
|
||||
|
||||
if attachments is not None:
|
||||
for attachment in attachments:
|
||||
if 'fallback' not in attachment:
|
||||
attachment['fallback'] = attachment['text']
|
||||
payload['attachments'].append(attachment)
|
||||
|
||||
payload = "payload=" + module.jsonify(payload)
|
||||
return payload
|
||||
|
||||
|
||||
def do_notify_rocketchat(module, domain, token, protocol, payload):
|
||||
|
||||
if token.count('/') < 1:
|
||||
module.fail_json(msg="Invalid Token specified, provide a valid token")
|
||||
|
||||
rocketchat_incoming_webhook = ROCKETCHAT_INCOMING_WEBHOOK % (protocol, domain, token)
|
||||
|
||||
response, info = fetch_url(module, rocketchat_incoming_webhook, data=payload)
|
||||
if info['status'] != 200:
|
||||
module.fail_json(msg="failed to send message, return status=%s" % str(info['status']))
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
domain=dict(type='str', required=True, default=None),
|
||||
token=dict(type='str', required=True, no_log=True),
|
||||
protocol=dict(type='str', default='https', choices=['http', 'https']),
|
||||
msg=dict(type='str', required=False, default=None),
|
||||
channel=dict(type='str', default=None),
|
||||
username=dict(type='str', default='Ansible'),
|
||||
icon_url=dict(type='str', default='https://www.ansible.com/favicon.ico'),
|
||||
icon_emoji=dict(type='str', default=None),
|
||||
link_names=dict(type='int', default=1, choices=[0, 1]),
|
||||
validate_certs=dict(default='yes', type='bool'),
|
||||
color=dict(type='str', default='normal', choices=['normal', 'good', 'warning', 'danger']),
|
||||
attachments=dict(type='list', required=False, default=None)
|
||||
)
|
||||
)
|
||||
|
||||
domain = module.params['domain']
|
||||
token = module.params['token']
|
||||
protocol = module.params['protocol']
|
||||
text = module.params['msg']
|
||||
channel = module.params['channel']
|
||||
username = module.params['username']
|
||||
icon_url = module.params['icon_url']
|
||||
icon_emoji = module.params['icon_emoji']
|
||||
link_names = module.params['link_names']
|
||||
color = module.params['color']
|
||||
attachments = module.params['attachments']
|
||||
|
||||
payload = build_payload_for_rocketchat(module, text, channel, username, icon_url, icon_emoji, link_names, color, attachments)
|
||||
do_notify_rocketchat(module, domain, token, protocol, payload)
|
||||
|
||||
module.exit_json(msg="OK")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
93
plugins/modules/notification/say.py
Normal file
93
plugins/modules/notification/say.py
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Michael DeHaan <michael@ansible.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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['stableinterface'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: say
|
||||
short_description: Makes a computer to speak.
|
||||
description:
|
||||
- makes a computer speak! Amuse your friends, annoy your coworkers!
|
||||
notes:
|
||||
- In 2.5, this module has been renamed from C(osx_say) to M(say).
|
||||
- If you like this module, you may also be interested in the osx_say callback plugin.
|
||||
- A list of available voices, with language, can be found by running C(say -v ?) on a OSX host and C(espeak --voices) on a Linux host.
|
||||
options:
|
||||
msg:
|
||||
description:
|
||||
What to say
|
||||
required: true
|
||||
voice:
|
||||
description:
|
||||
What voice to use
|
||||
required: false
|
||||
requirements: [ say or espeak or espeak-ng ]
|
||||
author:
|
||||
- "Ansible Core Team"
|
||||
- "Michael DeHaan (@mpdehaan)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- say:
|
||||
msg: '{{ inventory_hostname }} is all done'
|
||||
voice: Zarvox
|
||||
delegate_to: localhost
|
||||
'''
|
||||
import platform
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def say(module, executable, msg, voice):
|
||||
cmd = [executable, msg]
|
||||
if voice:
|
||||
cmd.extend(('-v', voice))
|
||||
module.run_command(cmd, check_rc=True)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
msg=dict(required=True),
|
||||
voice=dict(required=False),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
msg = module.params['msg']
|
||||
voice = module.params['voice']
|
||||
possibles = ('say', 'espeak', 'espeak-ng')
|
||||
|
||||
if platform.system() != 'Darwin':
|
||||
# 'say' binary available, it might be GNUstep tool which doesn't support 'voice' parameter
|
||||
voice = None
|
||||
|
||||
for possible in possibles:
|
||||
executable = module.get_bin_path(possible)
|
||||
if executable:
|
||||
break
|
||||
else:
|
||||
module.fail_json(msg='Unable to find either %s' % ', '.join(possibles))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(msg=msg, changed=False)
|
||||
|
||||
say(module, executable, msg, voice)
|
||||
|
||||
module.exit_json(msg=msg, changed=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
252
plugins/modules/notification/sendgrid.py
Normal file
252
plugins/modules/notification/sendgrid.py
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2015, Matt Makai <matthew.makai@gmail.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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: sendgrid
|
||||
short_description: Sends an email with the SendGrid API
|
||||
description:
|
||||
- "Sends an email with a SendGrid account through their API, not through
|
||||
the SMTP service."
|
||||
notes:
|
||||
- "This module is non-idempotent because it sends an email through the
|
||||
external API. It is idempotent only in the case that the module fails."
|
||||
- "Like the other notification modules, this one requires an external
|
||||
dependency to work. In this case, you'll need an active SendGrid
|
||||
account."
|
||||
- "In order to use api_key, cc, bcc, attachments, from_name, html_body, headers
|
||||
you must pip install sendgrid"
|
||||
- "since 2.2 username and password are not required if you supply an api_key"
|
||||
requirements:
|
||||
- sendgrid python library
|
||||
options:
|
||||
username:
|
||||
description:
|
||||
- username for logging into the SendGrid account.
|
||||
- Since 2.2 it is only required if api_key is not supplied.
|
||||
password:
|
||||
description:
|
||||
- password that corresponds to the username
|
||||
- Since 2.2 it is only required if api_key is not supplied.
|
||||
from_address:
|
||||
description:
|
||||
- the address in the "from" field for the email
|
||||
required: true
|
||||
to_addresses:
|
||||
description:
|
||||
- a list with one or more recipient email addresses
|
||||
required: true
|
||||
subject:
|
||||
description:
|
||||
- the desired subject for the email
|
||||
required: true
|
||||
api_key:
|
||||
description:
|
||||
- sendgrid API key to use instead of username/password
|
||||
cc:
|
||||
description:
|
||||
- a list of email addresses to cc
|
||||
bcc:
|
||||
description:
|
||||
- a list of email addresses to bcc
|
||||
attachments:
|
||||
description:
|
||||
- a list of relative or explicit paths of files you want to attach (7MB limit as per SendGrid docs)
|
||||
from_name:
|
||||
description:
|
||||
- the name you want to appear in the from field, i.e 'John Doe'
|
||||
html_body:
|
||||
description:
|
||||
- whether the body is html content that should be rendered
|
||||
type: bool
|
||||
default: 'no'
|
||||
headers:
|
||||
description:
|
||||
- a dict to pass on as headers
|
||||
author: "Matt Makai (@makaimc)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# send an email to a single recipient that the deployment was successful
|
||||
- sendgrid:
|
||||
username: "{{ sendgrid_username }}"
|
||||
password: "{{ sendgrid_password }}"
|
||||
from_address: "ansible@mycompany.com"
|
||||
to_addresses:
|
||||
- "ops@mycompany.com"
|
||||
subject: "Deployment success."
|
||||
body: "The most recent Ansible deployment was successful."
|
||||
delegate_to: localhost
|
||||
|
||||
# send an email to more than one recipient that the build failed
|
||||
- sendgrid:
|
||||
username: "{{ sendgrid_username }}"
|
||||
password: "{{ sendgrid_password }}"
|
||||
from_address: "build@mycompany.com"
|
||||
to_addresses:
|
||||
- "ops@mycompany.com"
|
||||
- "devteam@mycompany.com"
|
||||
subject: "Build failure!."
|
||||
body: "Unable to pull source repository from Git server."
|
||||
delegate_to: localhost
|
||||
'''
|
||||
|
||||
# =======================================
|
||||
# sendgrid module support methods
|
||||
#
|
||||
import os
|
||||
import traceback
|
||||
|
||||
SENDGRID_IMP_ERR = None
|
||||
try:
|
||||
import sendgrid
|
||||
HAS_SENDGRID = True
|
||||
except ImportError:
|
||||
SENDGRID_IMP_ERR = traceback.format_exc()
|
||||
HAS_SENDGRID = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
def post_sendgrid_api(module, username, password, from_address, to_addresses,
|
||||
subject, body, api_key=None, cc=None, bcc=None, attachments=None,
|
||||
html_body=False, from_name=None, headers=None):
|
||||
|
||||
if not HAS_SENDGRID:
|
||||
SENDGRID_URI = "https://api.sendgrid.com/api/mail.send.json"
|
||||
AGENT = "Ansible"
|
||||
data = {'api_user': username, 'api_key': password,
|
||||
'from': from_address, 'subject': subject, 'text': body}
|
||||
encoded_data = urlencode(data)
|
||||
to_addresses_api = ''
|
||||
for recipient in to_addresses:
|
||||
recipient = to_bytes(recipient, errors='surrogate_or_strict')
|
||||
to_addresses_api += '&to[]=%s' % recipient
|
||||
encoded_data += to_addresses_api
|
||||
|
||||
headers = {'User-Agent': AGENT,
|
||||
'Content-type': 'application/x-www-form-urlencoded',
|
||||
'Accept': 'application/json'}
|
||||
return fetch_url(module, SENDGRID_URI, data=encoded_data, headers=headers, method='POST')
|
||||
else:
|
||||
|
||||
if api_key:
|
||||
sg = sendgrid.SendGridClient(api_key)
|
||||
else:
|
||||
sg = sendgrid.SendGridClient(username, password)
|
||||
|
||||
message = sendgrid.Mail()
|
||||
message.set_subject(subject)
|
||||
|
||||
for recip in to_addresses:
|
||||
message.add_to(recip)
|
||||
|
||||
if cc:
|
||||
for recip in cc:
|
||||
message.add_cc(recip)
|
||||
if bcc:
|
||||
for recip in bcc:
|
||||
message.add_bcc(recip)
|
||||
|
||||
if headers:
|
||||
message.set_headers(headers)
|
||||
|
||||
if attachments:
|
||||
for f in attachments:
|
||||
name = os.path.basename(f)
|
||||
message.add_attachment(name, f)
|
||||
|
||||
if from_name:
|
||||
message.set_from('%s <%s.' % (from_name, from_address))
|
||||
else:
|
||||
message.set_from(from_address)
|
||||
|
||||
if html_body:
|
||||
message.set_html(body)
|
||||
else:
|
||||
message.set_text(body)
|
||||
|
||||
return sg.send(message)
|
||||
# =======================================
|
||||
# Main
|
||||
#
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
username=dict(required=False),
|
||||
password=dict(required=False, no_log=True),
|
||||
api_key=dict(required=False, no_log=True),
|
||||
bcc=dict(required=False, type='list'),
|
||||
cc=dict(required=False, type='list'),
|
||||
headers=dict(required=False, type='dict'),
|
||||
from_address=dict(required=True),
|
||||
from_name=dict(required=False),
|
||||
to_addresses=dict(required=True, type='list'),
|
||||
subject=dict(required=True),
|
||||
body=dict(required=True),
|
||||
html_body=dict(required=False, default=False, type='bool'),
|
||||
attachments=dict(required=False, type='list')
|
||||
),
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[
|
||||
['api_key', 'password'],
|
||||
['api_key', 'username']
|
||||
],
|
||||
required_together=[['username', 'password']],
|
||||
)
|
||||
|
||||
username = module.params['username']
|
||||
password = module.params['password']
|
||||
api_key = module.params['api_key']
|
||||
bcc = module.params['bcc']
|
||||
cc = module.params['cc']
|
||||
headers = module.params['headers']
|
||||
from_name = module.params['from_name']
|
||||
from_address = module.params['from_address']
|
||||
to_addresses = module.params['to_addresses']
|
||||
subject = module.params['subject']
|
||||
body = module.params['body']
|
||||
html_body = module.params['html_body']
|
||||
attachments = module.params['attachments']
|
||||
|
||||
sendgrid_lib_args = [api_key, bcc, cc, headers, from_name, html_body, attachments]
|
||||
|
||||
if any(lib_arg is not None for lib_arg in sendgrid_lib_args) and not HAS_SENDGRID:
|
||||
reason = 'when using any of the following arguments: ' \
|
||||
'api_key, bcc, cc, headers, from_name, html_body, attachments'
|
||||
module.fail_json(msg=missing_required_lib('sendgrid', reason=reason),
|
||||
exception=SENDGRID_IMP_ERR)
|
||||
|
||||
response, info = post_sendgrid_api(module, username, password,
|
||||
from_address, to_addresses, subject, body, attachments=attachments,
|
||||
bcc=bcc, cc=cc, headers=headers, html_body=html_body, api_key=api_key)
|
||||
|
||||
if not HAS_SENDGRID:
|
||||
if info['status'] != 200:
|
||||
module.fail_json(msg="unable to send email through SendGrid API: %s" % info['msg'])
|
||||
else:
|
||||
if response != 200:
|
||||
module.fail_json(msg="unable to send email through SendGrid API: %s" % info['message'])
|
||||
|
||||
module.exit_json(msg=subject, changed=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
308
plugins/modules/notification/slack.py
Normal file
308
plugins/modules/notification/slack.py
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2017, Steve Pletcher <steve@steve-pletcher.com>
|
||||
# (c) 2016, René Moser <mail@renemoser.net>
|
||||
# (c) 2015, Stefan Berggren <nsg@nsg.cc>
|
||||
# (c) 2014, Ramon de la Fuente <ramon@delafuente.nl>
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['stableinterface'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: slack
|
||||
short_description: Send Slack notifications
|
||||
description:
|
||||
- The C(slack) module sends notifications to U(http://slack.com) via the Incoming WebHook integration
|
||||
author: "Ramon de la Fuente (@ramondelafuente)"
|
||||
options:
|
||||
domain:
|
||||
description:
|
||||
- Slack (sub)domain for your environment without protocol. (i.e.
|
||||
C(example.slack.com)) In 1.8 and beyond, this is deprecated and may
|
||||
be ignored. See token documentation for information.
|
||||
token:
|
||||
description:
|
||||
- Slack integration token. This authenticates you to the slack service.
|
||||
Prior to 1.8, a token looked like C(3Ffe373sfhRE6y42Fg3rvf4GlK). In
|
||||
1.8 and above, ansible adapts to the new slack API where tokens look
|
||||
like C(G922VJP24/D921DW937/3Ffe373sfhRE6y42Fg3rvf4GlK). If tokens
|
||||
are in the new format then slack will ignore any value of domain. If
|
||||
the token is in the old format the domain is required. Ansible has no
|
||||
control of when slack will get rid of the old API. When slack does
|
||||
that the old format will stop working. ** Please keep in mind the tokens
|
||||
are not the API tokens but are the webhook tokens. In slack these are
|
||||
found in the webhook URL which are obtained under the apps and integrations.
|
||||
The incoming webhooks can be added in that area. In some cases this may
|
||||
be locked by your Slack admin and you must request access. It is there
|
||||
that the incoming webhooks can be added. The key is on the end of the
|
||||
URL given to you in that section.
|
||||
required: true
|
||||
msg:
|
||||
description:
|
||||
- Message to send. Note that the module does not handle escaping characters.
|
||||
Plain-text angle brackets and ampersands should be converted to HTML entities (e.g. & to &) before sending.
|
||||
See Slack's documentation (U(https://api.slack.com/docs/message-formatting)) for more.
|
||||
channel:
|
||||
description:
|
||||
- Channel to send the message to. If absent, the message goes to the channel selected for the I(token).
|
||||
thread_id:
|
||||
description:
|
||||
- Optional. Timestamp of message to thread this message to as a float. https://api.slack.com/docs/message-threading
|
||||
username:
|
||||
description:
|
||||
- This is the sender of the message.
|
||||
default: "Ansible"
|
||||
icon_url:
|
||||
description:
|
||||
- Url for the message sender's icon (default C(https://www.ansible.com/favicon.ico))
|
||||
icon_emoji:
|
||||
description:
|
||||
- Emoji for the message sender. See Slack documentation for options.
|
||||
(if I(icon_emoji) is set, I(icon_url) will not be used)
|
||||
link_names:
|
||||
description:
|
||||
- Automatically create links for channels and usernames in I(msg).
|
||||
default: 1
|
||||
choices:
|
||||
- 1
|
||||
- 0
|
||||
parse:
|
||||
description:
|
||||
- Setting for the message parser at Slack
|
||||
choices:
|
||||
- 'full'
|
||||
- 'none'
|
||||
validate_certs:
|
||||
description:
|
||||
- If C(no), SSL certificates will not be validated. This should only be used
|
||||
on personally controlled sites using self-signed certificates.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
color:
|
||||
description:
|
||||
- Allow text to use default colors - use the default of 'normal' to not send a custom color bar at the start of the message.
|
||||
- Allowed values for color can be one of 'normal', 'good', 'warning', 'danger', any valid 3 digit or 6 digit hex color value.
|
||||
- Specifying value in hex is supported from version 2.8.
|
||||
default: 'normal'
|
||||
attachments:
|
||||
description:
|
||||
- Define a list of attachments. This list mirrors the Slack JSON API.
|
||||
- For more information, see also in the (U(https://api.slack.com/docs/attachments)).
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Send notification message via Slack
|
||||
slack:
|
||||
token: thetoken/generatedby/slack
|
||||
msg: '{{ inventory_hostname }} completed'
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Send notification message via Slack all options
|
||||
slack:
|
||||
token: thetoken/generatedby/slack
|
||||
msg: '{{ inventory_hostname }} completed'
|
||||
channel: '#ansible'
|
||||
thread_id: 1539917263.000100
|
||||
username: 'Ansible on {{ inventory_hostname }}'
|
||||
icon_url: http://www.example.com/some-image-file.png
|
||||
link_names: 0
|
||||
parse: 'none'
|
||||
delegate_to: localhost
|
||||
|
||||
- name: insert a color bar in front of the message for visibility purposes and use the default webhook icon and name configured in Slack
|
||||
slack:
|
||||
token: thetoken/generatedby/slack
|
||||
msg: '{{ inventory_hostname }} is alive!'
|
||||
color: good
|
||||
username: ''
|
||||
icon_url: ''
|
||||
|
||||
- name: insert a color bar in front of the message with valid hex color value
|
||||
slack:
|
||||
token: thetoken/generatedby/slack
|
||||
msg: 'This message uses color in hex value'
|
||||
color: '#00aacc'
|
||||
username: ''
|
||||
icon_url: ''
|
||||
|
||||
- name: Use the attachments API
|
||||
slack:
|
||||
token: thetoken/generatedby/slack
|
||||
attachments:
|
||||
- text: Display my system load on host A and B
|
||||
color: '#ff00dd'
|
||||
title: System load
|
||||
fields:
|
||||
- title: System A
|
||||
value: "load average: 0,74, 0,66, 0,63"
|
||||
short: True
|
||||
- title: System B
|
||||
value: 'load average: 5,16, 4,64, 2,43'
|
||||
short: True
|
||||
|
||||
- name: Send a message with a link using Slack markup
|
||||
slack:
|
||||
token: thetoken/generatedby/slack
|
||||
msg: We sent this message using <https://www.ansible.com|Ansible>!
|
||||
|
||||
- name: Send a message with angle brackets and ampersands
|
||||
slack:
|
||||
token: thetoken/generatedby/slack
|
||||
msg: This message has <brackets> & ampersands in plain text.
|
||||
"""
|
||||
|
||||
import re
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
OLD_SLACK_INCOMING_WEBHOOK = 'https://%s/services/hooks/incoming-webhook?token=%s'
|
||||
SLACK_INCOMING_WEBHOOK = 'https://hooks.slack.com/services/%s'
|
||||
|
||||
# Escaping quotes and apostrophes to avoid ending string prematurely in ansible call.
|
||||
# We do not escape other characters used as Slack metacharacters (e.g. &, <, >).
|
||||
escape_table = {
|
||||
'"': "\"",
|
||||
"'": "\'",
|
||||
}
|
||||
|
||||
|
||||
def is_valid_hex_color(color_choice):
|
||||
if re.match(r'^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$', color_choice):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def escape_quotes(text):
|
||||
'''Backslash any quotes within text.'''
|
||||
return "".join(escape_table.get(c, c) for c in text)
|
||||
|
||||
|
||||
def build_payload_for_slack(module, text, channel, thread_id, username, icon_url, icon_emoji, link_names,
|
||||
parse, color, attachments):
|
||||
payload = {}
|
||||
if color == "normal" and text is not None:
|
||||
payload = dict(text=escape_quotes(text))
|
||||
elif text is not None:
|
||||
# With a custom color we have to set the message as attachment, and explicitly turn markdown parsing on for it.
|
||||
payload = dict(attachments=[dict(text=escape_quotes(text), color=color, mrkdwn_in=["text"])])
|
||||
if channel is not None:
|
||||
if (channel[0] == '#') or (channel[0] == '@'):
|
||||
payload['channel'] = channel
|
||||
else:
|
||||
payload['channel'] = '#' + channel
|
||||
if thread_id is not None:
|
||||
payload['thread_ts'] = thread_id
|
||||
if username is not None:
|
||||
payload['username'] = username
|
||||
if icon_emoji is not None:
|
||||
payload['icon_emoji'] = icon_emoji
|
||||
else:
|
||||
payload['icon_url'] = icon_url
|
||||
if link_names is not None:
|
||||
payload['link_names'] = link_names
|
||||
if parse is not None:
|
||||
payload['parse'] = parse
|
||||
|
||||
if attachments is not None:
|
||||
if 'attachments' not in payload:
|
||||
payload['attachments'] = []
|
||||
|
||||
if attachments is not None:
|
||||
keys_to_escape = [
|
||||
'title',
|
||||
'text',
|
||||
'author_name',
|
||||
'pretext',
|
||||
'fallback',
|
||||
]
|
||||
for attachment in attachments:
|
||||
for key in keys_to_escape:
|
||||
if key in attachment:
|
||||
attachment[key] = escape_quotes(attachment[key])
|
||||
|
||||
if 'fallback' not in attachment:
|
||||
attachment['fallback'] = attachment['text']
|
||||
|
||||
payload['attachments'].append(attachment)
|
||||
|
||||
payload = module.jsonify(payload)
|
||||
return payload
|
||||
|
||||
|
||||
def do_notify_slack(module, domain, token, payload):
|
||||
if token.count('/') >= 2:
|
||||
# New style token
|
||||
slack_incoming_webhook = SLACK_INCOMING_WEBHOOK % (token)
|
||||
else:
|
||||
if not domain:
|
||||
module.fail_json(msg="Slack has updated its webhook API. You need to specify a token of the form "
|
||||
"XXXX/YYYY/ZZZZ in your playbook")
|
||||
slack_incoming_webhook = OLD_SLACK_INCOMING_WEBHOOK % (domain, token)
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
response, info = fetch_url(module=module, url=slack_incoming_webhook, headers=headers, method='POST', data=payload)
|
||||
|
||||
if info['status'] != 200:
|
||||
obscured_incoming_webhook = SLACK_INCOMING_WEBHOOK % ('[obscured]')
|
||||
module.fail_json(msg=" failed to send %s to %s: %s" % (payload, obscured_incoming_webhook, info['msg']))
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
domain=dict(type='str', required=False, default=None),
|
||||
token=dict(type='str', required=True, no_log=True),
|
||||
msg=dict(type='str', required=False, default=None),
|
||||
channel=dict(type='str', default=None),
|
||||
thread_id=dict(type='float', default=None),
|
||||
username=dict(type='str', default='Ansible'),
|
||||
icon_url=dict(type='str', default='https://www.ansible.com/favicon.ico'),
|
||||
icon_emoji=dict(type='str', default=None),
|
||||
link_names=dict(type='int', default=1, choices=[0, 1]),
|
||||
parse=dict(type='str', default=None, choices=['none', 'full']),
|
||||
validate_certs=dict(default='yes', type='bool'),
|
||||
color=dict(type='str', default='normal'),
|
||||
attachments=dict(type='list', required=False, default=None)
|
||||
)
|
||||
)
|
||||
|
||||
domain = module.params['domain']
|
||||
token = module.params['token']
|
||||
text = module.params['msg']
|
||||
channel = module.params['channel']
|
||||
thread_id = module.params['thread_id']
|
||||
username = module.params['username']
|
||||
icon_url = module.params['icon_url']
|
||||
icon_emoji = module.params['icon_emoji']
|
||||
link_names = module.params['link_names']
|
||||
parse = module.params['parse']
|
||||
color = module.params['color']
|
||||
attachments = module.params['attachments']
|
||||
|
||||
color_choices = ['normal', 'good', 'warning', 'danger']
|
||||
if color not in color_choices and not is_valid_hex_color(color):
|
||||
module.fail_json(msg="Color value specified should be either one of %r "
|
||||
"or any valid hex value with length 3 or 6." % color_choices)
|
||||
|
||||
payload = build_payload_for_slack(module, text, channel, thread_id, username, icon_url, icon_emoji, link_names,
|
||||
parse, color, attachments)
|
||||
do_notify_slack(module, domain, token, payload)
|
||||
|
||||
module.exit_json(msg="OK")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
167
plugins/modules/notification/syslogger.py
Normal file
167
plugins/modules/notification/syslogger.py
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (c) 2017 Tim Rightnour <thegarbledone@gmail.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
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: syslogger
|
||||
|
||||
short_description: Log messages in the syslog
|
||||
|
||||
|
||||
description:
|
||||
- "Uses syslog to add log entries to the host."
|
||||
- "Can specify facility and priority."
|
||||
|
||||
options:
|
||||
msg:
|
||||
description:
|
||||
- This is the message to place in syslog
|
||||
required: true
|
||||
priority:
|
||||
description:
|
||||
- Set the log priority
|
||||
choices: [ "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug" ]
|
||||
required: false
|
||||
default: "info"
|
||||
facility:
|
||||
description:
|
||||
- Set the log facility
|
||||
choices: [ "kern", "user", "mail", "daemon", "auth", "lpr", "news",
|
||||
"uucp", "cron", "syslog", "local0", "local1", "local2",
|
||||
"local3", "local4", "local5", "local6", "local7" ]
|
||||
required: false
|
||||
default: "daemon"
|
||||
log_pid:
|
||||
description:
|
||||
- Log the pid in brackets
|
||||
type: bool
|
||||
required: false
|
||||
default: "no"
|
||||
|
||||
author:
|
||||
- Tim Rightnour (@garbled1)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Full example
|
||||
- name: Test syslog
|
||||
syslogger:
|
||||
msg: "Hello from ansible"
|
||||
priority: "err"
|
||||
facility: "daemon"
|
||||
log_pid: true
|
||||
|
||||
# Basic usage
|
||||
- name: Simple Usage
|
||||
syslogger:
|
||||
msg: "I will end up as daemon.info"
|
||||
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
import syslog
|
||||
|
||||
|
||||
def get_facility(x):
|
||||
return {
|
||||
'kern': syslog.LOG_KERN,
|
||||
'user': syslog.LOG_USER,
|
||||
'mail': syslog.LOG_MAIL,
|
||||
'daemon': syslog.LOG_DAEMON,
|
||||
'auth': syslog.LOG_AUTH,
|
||||
'lpr': syslog.LOG_LPR,
|
||||
'news': syslog.LOG_NEWS,
|
||||
'uucp': syslog.LOG_UUCP,
|
||||
'cron': syslog.LOG_CRON,
|
||||
'syslog': syslog.LOG_SYSLOG,
|
||||
'local0': syslog.LOG_LOCAL0,
|
||||
'local1': syslog.LOG_LOCAL1,
|
||||
'local2': syslog.LOG_LOCAL2,
|
||||
'local3': syslog.LOG_LOCAL3,
|
||||
'local4': syslog.LOG_LOCAL4,
|
||||
'local5': syslog.LOG_LOCAL5,
|
||||
'local6': syslog.LOG_LOCAL6,
|
||||
'local7': syslog.LOG_LOCAL7
|
||||
}.get(x, syslog.LOG_DAEMON)
|
||||
|
||||
|
||||
def get_priority(x):
|
||||
return {
|
||||
'emerg': syslog.LOG_EMERG,
|
||||
'alert': syslog.LOG_ALERT,
|
||||
'crit': syslog.LOG_CRIT,
|
||||
'err': syslog.LOG_ERR,
|
||||
'warning': syslog.LOG_WARNING,
|
||||
'notice': syslog.LOG_NOTICE,
|
||||
'info': syslog.LOG_INFO,
|
||||
'debug': syslog.LOG_DEBUG
|
||||
}.get(x, syslog.LOG_INFO)
|
||||
|
||||
|
||||
def run_module():
|
||||
# define the available arguments/parameters that a user can pass to
|
||||
# the module
|
||||
module_args = dict(
|
||||
msg=dict(type='str', required=True),
|
||||
priority=dict(type='str', required=False,
|
||||
choices=["emerg", "alert", "crit", "err", "warning",
|
||||
"notice", "info", "debug"],
|
||||
default='info'),
|
||||
facility=dict(type='str', required=False,
|
||||
choices=["kern", "user", "mail", "daemon", "auth",
|
||||
"lpr", "news", "uucp", "cron", "syslog",
|
||||
"local0", "local1", "local2", "local3",
|
||||
"local4", "local5", "local6", "local7"],
|
||||
default='daemon'),
|
||||
log_pid=dict(type='bool', required=False, default=False)
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
)
|
||||
|
||||
result = dict(
|
||||
changed=False,
|
||||
priority=module.params['priority'],
|
||||
facility=module.params['facility'],
|
||||
log_pid=module.params['log_pid'],
|
||||
msg=module.params['msg']
|
||||
)
|
||||
|
||||
# do the logging
|
||||
try:
|
||||
if module.params['log_pid']:
|
||||
syslog.openlog('ansible_syslogger',
|
||||
logoption=syslog.LOG_PID,
|
||||
facility=get_facility(module.params['facility']))
|
||||
else:
|
||||
syslog.openlog('ansible_syslogger',
|
||||
facility=get_facility(module.params['facility']))
|
||||
syslog.syslog(get_priority(module.params['priority']),
|
||||
module.params['msg'])
|
||||
syslog.closelog()
|
||||
result['changed'] = True
|
||||
|
||||
except Exception:
|
||||
module.fail_json(error='Failed to write to syslog', **result)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
run_module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
115
plugins/modules/notification/telegram.py
Normal file
115
plugins/modules/notification/telegram.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2016, Artem Feofanov <artem.feofanov@gmail.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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
|
||||
module: telegram
|
||||
author: "Artem Feofanov (@tyouxa)"
|
||||
|
||||
short_description: module for sending notifications via telegram
|
||||
|
||||
description:
|
||||
- Send notifications via telegram bot, to a verified group or user
|
||||
notes:
|
||||
- You will require a telegram account and create telegram bot to use this module.
|
||||
options:
|
||||
msg:
|
||||
description:
|
||||
- What message you wish to send.
|
||||
required: true
|
||||
msg_format:
|
||||
description:
|
||||
- Message format. Formatting options `markdown` and `html` described in
|
||||
Telegram API docs (https://core.telegram.org/bots/api#formatting-options).
|
||||
If option `plain` set, message will not be formatted.
|
||||
default: plain
|
||||
choices: [ "plain", "markdown", "html" ]
|
||||
token:
|
||||
description:
|
||||
- Token identifying your telegram bot.
|
||||
required: true
|
||||
chat_id:
|
||||
description:
|
||||
- Telegram group or user chat_id
|
||||
required: true
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
|
||||
- name: send a message to chat in playbook
|
||||
telegram:
|
||||
token: '9999999:XXXXXXXXXXXXXXXXXXXXXXX'
|
||||
chat_id: 000000
|
||||
msg: Ansible task finished
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
|
||||
msg:
|
||||
description: The message you attempted to send
|
||||
returned: success
|
||||
type: str
|
||||
sample: "Ansible task finished"
|
||||
telegram_error:
|
||||
description: Error message gotten from Telegram API
|
||||
returned: failure
|
||||
type: str
|
||||
sample: "Bad Request: message text is empty"
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six.moves.urllib.parse import quote
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
token=dict(type='str', required=True, no_log=True),
|
||||
chat_id=dict(type='str', required=True, no_log=True),
|
||||
msg_format=dict(type='str', required=False, default='plain',
|
||||
choices=['plain', 'markdown', 'html']),
|
||||
msg=dict(type='str', required=True)),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
token = quote(module.params.get('token'))
|
||||
chat_id = quote(module.params.get('chat_id'))
|
||||
msg_format = quote(module.params.get('msg_format'))
|
||||
msg = quote(module.params.get('msg'))
|
||||
|
||||
url = 'https://api.telegram.org/bot' + token + \
|
||||
'/sendMessage?text=' + msg + '&chat_id=' + chat_id
|
||||
if msg_format in ('markdown', 'html'):
|
||||
url += '&parse_mode=' + msg_format
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=False)
|
||||
|
||||
response, info = fetch_url(module, url)
|
||||
if info['status'] == 200:
|
||||
module.exit_json(changed=True)
|
||||
else:
|
||||
body = json.loads(info['body'])
|
||||
module.fail_json(msg="failed to send message, return status=%s" % str(info['status']),
|
||||
telegram_error=body['description'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
169
plugins/modules/notification/twilio.py
Normal file
169
plugins/modules/notification/twilio.py
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2015, Matt Makai <matthew.makai@gmail.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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: twilio
|
||||
short_description: Sends a text message to a mobile phone through Twilio.
|
||||
description:
|
||||
- Sends a text message to a phone number through the Twilio messaging API.
|
||||
notes:
|
||||
- This module is non-idempotent because it sends an email through the
|
||||
external API. It is idempotent only in the case that the module fails.
|
||||
- Like the other notification modules, this one requires an external
|
||||
dependency to work. In this case, you'll need a Twilio account with
|
||||
a purchased or verified phone number to send the text message.
|
||||
options:
|
||||
account_sid:
|
||||
description:
|
||||
user's Twilio account token found on the account page
|
||||
required: true
|
||||
auth_token:
|
||||
description: user's Twilio authentication token
|
||||
required: true
|
||||
msg:
|
||||
description:
|
||||
the body of the text message
|
||||
required: true
|
||||
to_numbers:
|
||||
description:
|
||||
one or more phone numbers to send the text message to,
|
||||
format +15551112222
|
||||
required: true
|
||||
aliases: [ to_number ]
|
||||
from_number:
|
||||
description:
|
||||
the Twilio number to send the text message from, format +15551112222
|
||||
required: true
|
||||
media_url:
|
||||
description:
|
||||
a URL with a picture, video or sound clip to send with an MMS
|
||||
(multimedia message) instead of a plain SMS
|
||||
required: false
|
||||
|
||||
author: "Matt Makai (@makaimc)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# send an SMS about the build status to (555) 303 5681
|
||||
# note: replace account_sid and auth_token values with your credentials
|
||||
# and you have to have the 'from_number' on your Twilio account
|
||||
- twilio:
|
||||
msg: All servers with webserver role are now configured.
|
||||
account_sid: ACXXXXXXXXXXXXXXXXX
|
||||
auth_token: ACXXXXXXXXXXXXXXXXX
|
||||
from_number: +15552014545
|
||||
to_number: +15553035681
|
||||
delegate_to: localhost
|
||||
|
||||
# send an SMS to multiple phone numbers about the deployment
|
||||
# note: replace account_sid and auth_token values with your credentials
|
||||
# and you have to have the 'from_number' on your Twilio account
|
||||
- twilio:
|
||||
msg: This server configuration is now complete.
|
||||
account_sid: ACXXXXXXXXXXXXXXXXX
|
||||
auth_token: ACXXXXXXXXXXXXXXXXX
|
||||
from_number: +15553258899
|
||||
to_numbers:
|
||||
- +15551113232
|
||||
- +12025551235
|
||||
- +19735559010
|
||||
delegate_to: localhost
|
||||
|
||||
# send an MMS to a single recipient with an update on the deployment
|
||||
# and an image of the results
|
||||
# note: replace account_sid and auth_token values with your credentials
|
||||
# and you have to have the 'from_number' on your Twilio account
|
||||
- twilio:
|
||||
msg: Deployment complete!
|
||||
account_sid: ACXXXXXXXXXXXXXXXXX
|
||||
auth_token: ACXXXXXXXXXXXXXXXXX
|
||||
from_number: +15552014545
|
||||
to_number: +15553035681
|
||||
media_url: https://demo.twilio.com/logo.png
|
||||
delegate_to: localhost
|
||||
'''
|
||||
|
||||
# =======================================
|
||||
# twilio module support methods
|
||||
#
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
def post_twilio_api(module, account_sid, auth_token, msg, from_number,
|
||||
to_number, media_url=None):
|
||||
URI = "https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json" \
|
||||
% (account_sid,)
|
||||
AGENT = "Ansible"
|
||||
|
||||
data = {'From': from_number, 'To': to_number, 'Body': msg}
|
||||
if media_url:
|
||||
data['MediaUrl'] = media_url
|
||||
encoded_data = urlencode(data)
|
||||
|
||||
headers = {'User-Agent': AGENT,
|
||||
'Content-type': 'application/x-www-form-urlencoded',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
|
||||
# Hack module params to have the Basic auth params that fetch_url expects
|
||||
module.params['url_username'] = account_sid.replace('\n', '')
|
||||
module.params['url_password'] = auth_token.replace('\n', '')
|
||||
|
||||
return fetch_url(module, URI, data=encoded_data, headers=headers)
|
||||
|
||||
|
||||
# =======================================
|
||||
# Main
|
||||
#
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
account_sid=dict(required=True),
|
||||
auth_token=dict(required=True, no_log=True),
|
||||
msg=dict(required=True),
|
||||
from_number=dict(required=True),
|
||||
to_numbers=dict(required=True, aliases=['to_number'], type='list'),
|
||||
media_url=dict(default=None, required=False),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
account_sid = module.params['account_sid']
|
||||
auth_token = module.params['auth_token']
|
||||
msg = module.params['msg']
|
||||
from_number = module.params['from_number']
|
||||
to_numbers = module.params['to_numbers']
|
||||
media_url = module.params['media_url']
|
||||
|
||||
for number in to_numbers:
|
||||
r, info = post_twilio_api(module, account_sid, auth_token, msg,
|
||||
from_number, number, media_url)
|
||||
if info['status'] not in [200, 201]:
|
||||
body_message = "unknown error"
|
||||
if 'body' in info:
|
||||
body = module.from_json(info['body'])
|
||||
body_message = body['message']
|
||||
module.fail_json(msg="unable to send message to %s: %s" % (number, body_message))
|
||||
|
||||
module.exit_json(msg=msg, changed=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
128
plugins/modules/notification/typetalk.py
Normal file
128
plugins/modules/notification/typetalk.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright: Ansible Project
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: typetalk
|
||||
short_description: Send a message to typetalk
|
||||
description:
|
||||
- Send a message to typetalk using typetalk API
|
||||
options:
|
||||
client_id:
|
||||
description:
|
||||
- OAuth2 client ID
|
||||
required: true
|
||||
client_secret:
|
||||
description:
|
||||
- OAuth2 client secret
|
||||
required: true
|
||||
topic:
|
||||
description:
|
||||
- topic id to post message
|
||||
required: true
|
||||
msg:
|
||||
description:
|
||||
- message body
|
||||
required: true
|
||||
requirements: [ json ]
|
||||
author: "Takashi Someda (@tksmd)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- typetalk:
|
||||
client_id: 12345
|
||||
client_secret: 12345
|
||||
topic: 1
|
||||
msg: install completed
|
||||
'''
|
||||
|
||||
import json
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.urls import fetch_url, ConnectionError
|
||||
|
||||
|
||||
def do_request(module, url, params, headers=None):
|
||||
data = urlencode(params)
|
||||
if headers is None:
|
||||
headers = dict()
|
||||
headers = dict(headers, **{
|
||||
'User-Agent': 'Ansible/typetalk module',
|
||||
})
|
||||
r, info = fetch_url(module, url, data=data, headers=headers)
|
||||
if info['status'] != 200:
|
||||
exc = ConnectionError(info['msg'])
|
||||
exc.code = info['status']
|
||||
raise exc
|
||||
return r
|
||||
|
||||
|
||||
def get_access_token(module, client_id, client_secret):
|
||||
params = {
|
||||
'client_id': client_id,
|
||||
'client_secret': client_secret,
|
||||
'grant_type': 'client_credentials',
|
||||
'scope': 'topic.post'
|
||||
}
|
||||
res = do_request(module, 'https://typetalk.com/oauth2/access_token', params)
|
||||
return json.load(res)['access_token']
|
||||
|
||||
|
||||
def send_message(module, client_id, client_secret, topic, msg):
|
||||
"""
|
||||
send message to typetalk
|
||||
"""
|
||||
try:
|
||||
access_token = get_access_token(module, client_id, client_secret)
|
||||
url = 'https://typetalk.com/api/v1/topics/%d' % topic
|
||||
headers = {
|
||||
'Authorization': 'Bearer %s' % access_token,
|
||||
}
|
||||
do_request(module, url, {'message': msg}, headers)
|
||||
return True, {'access_token': access_token}
|
||||
except ConnectionError as e:
|
||||
return False, e
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
client_id=dict(required=True),
|
||||
client_secret=dict(required=True, no_log=True),
|
||||
topic=dict(required=True, type='int'),
|
||||
msg=dict(required=True),
|
||||
),
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
||||
if not json:
|
||||
module.fail_json(msg="json module is required")
|
||||
|
||||
client_id = module.params["client_id"]
|
||||
client_secret = module.params["client_secret"]
|
||||
topic = module.params["topic"]
|
||||
msg = module.params["msg"]
|
||||
|
||||
res, error = send_message(module, client_id, client_secret, topic, msg)
|
||||
if not res:
|
||||
module.fail_json(msg='fail to send message with response code %s' % error.code)
|
||||
|
||||
module.exit_json(changed=True, topic=topic, msg=msg)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue