from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = ''' callback: telegram callback_type: notification requirements: - whitelist in configuration - telebot (pip install pyTelegramBotApi) - prettytable (pip install prettytable) - latest requests (pip install requests --upgrade) short_description: Sends play events to a telegram channel version_added: "2.1" description: - This is an ansible callback plugin that sends status updates to a telegram channel during playbook execution. - Before 2.4 only environment variables were available for configuring this plugin options: tg_token: required: True description: telegram bot token env: - name: TG_TOKEN ini: - section: callback_telegram key: tg_token tg_chat_id: required: True description: telegram chat id to post in. env: - name: TG_CHAT_ID ini: - section: callback_telegram key: tg_chat_id socks5_uri: description: socks5 proxy uri to bypass rkn's restarictions env: - name: SOCKS5_URI ini: - section: callback_telegram key: socks5_uri ''' import os from datetime import datetime from ansible import context from ansible.module_utils._text import to_text from ansible.module_utils.urls import open_url from ansible.plugins.callback import CallbackBase try: import telebot from telebot import apihelper HAS_TELEBOT = True except ImportError: HAS_TELEBOT = False try: import prettytable HAS_PRETTYTABLE = True except ImportError: HAS_PRETTYTABLE = False class CallbackModule(CallbackBase): """This is an ansible callback plugin that sends status updates to a telegram channel during playbook execution. """ CALLBACK_VERSION = 2.0 CALLBACK_TYPE = 'notification' CALLBACK_NAME = 'telegram' CALLBACK_NEEDS_WHITELIST = True def __init__(self, display=None): super(CallbackModule, self).__init__(display=display) if not HAS_TELEBOT: self.disabled = True self._display.warning('The `telebot` python module is not ' 'installed. Disabling the Slack callback ' 'plugin.') if not HAS_PRETTYTABLE: self.disabled = True self._display.warning('The `prettytable` python module is not ' 'installed. Disabling the Slack callback ' 'plugin.') self.playbook_name = None self.play = None self.now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") def set_options(self, task_keys=None, var_options=None, direct=None): super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct) self.tg_token = self.get_option('tg_token') self.tg_chat_id = self.get_option('tg_chat_id') self.socks5_uri = self.get_option('socks5_uri') if self.tg_token is None: self.disabled = True self._display.warning('tg_token was not provided. The ' 'tg_token can be provided using ' 'the `TG_TOKEN` environment ' 'variable.') if self.tg_chat_id is None: self.disabled = True self._display.warning('tg_chat_id was not provided. The ' 'tg_chat_id can be provided using ' 'the `TG_CHAT_ID` environment ' 'variable.') def send_msg(self, msg): if self.socks5_uri is not None: apihelper.proxy = {'https': self.socks5_uri} # print(self.tg_token) bot = telebot.TeleBot(self.tg_token) # print(bot) # print(self.tg_chat_id) # print(msg) # bot.send_message(self.tg_chat_id, 'Hi! I\'m a Bot!') bot.send_message(self.tg_chat_id, msg, parse_mode='HTML') def v2_playbook_on_start(self, playbook): self.playbook_name = os.path.abspath(playbook._file_name) def v2_playbook_on_play_start(self, play): self.play = play title = [ 'Ansible: STARTED ⚙️' ] msg_items = [' '.join(title)] msg_items.append('\n time: ' + '' + str(self.now) + '') msg_items.append('playbook: ' + '' + self.playbook_name + '') msg_items.append(' hosts:') for host in play.hosts: msg_items.append(' - ' + host + '') msg_items.append(' tags:') for tag in play.only_tags: msg_items.append(' - ' + tag + '') msg = '\n'.join(msg_items) self.send_msg(msg=msg) def v2_runner_on_failed(self, result, ignore_errors=False): msg = [] title = [ 'Ansible: FAILED ❌' ] msg_items = [' '.join(title)] msg_items.append('\n time: ' + '' + str(self.now) + '') msg_items.append('playbook: ' + '' + self.playbook_name + '') msg_items.append(' host: ' + '' + result._host.get_name() + '') msg_items.append(' stderr: ' + '' + result._result['stderr'] + '') msg = '\n'.join(msg_items) self.send_msg(msg=msg) def v2_playbook_on_stats(self, stats): """Display info about playbook statistics""" hosts = sorted(stats.processed.keys()) t = prettytable.PrettyTable(['Host and state'] ) #, 'Ok', 'Changed', 'Unreachable', # 'Failures', 'Rescued', 'Ignored']) failures = False unreachable = False for h in hosts: s = stats.summarize(h) if s['failures'] > 0: failures = True if s['unreachable'] > 0: unreachable = True print(s) t.add_row([h.replace('.guaranteedstruggle.host','')] ) print([h.replace('.guaranteedstruggle.host','')]) print(', '.join([ str(s[k]) for k in ['ok', 'changed', 'unreachable', 'failures', 'rescued', 'ignored']])) t.add_row( ["[" + ', '.join([ str(s[k]) for k in ['ok', 'changed', 'unreachable', 'failures', 'rescued', 'ignored']]) + "]"]) msg = [] title = 'Ansible: ENDED' if failures or unreachable: msg_items = [ title + ' ❌' ] else: msg_items = [ title + ' ✅' ] msg_items.append('\n time: ' + '' + str(self.now) + '') msg_items.append('playbook: ' + '' + self.playbook_name + '') msg_items.append('\n%s\n' % t) msg_items.append('' + 'ok,chg,unr,fail,res,ign' + '') msg = '\n'.join(msg_items) self.send_msg(msg=msg)