Skip to content

Commit

Permalink
feat(notify): support rendering markdown for notifications (#42)
Browse files Browse the repository at this point in the history
* feat(icinga): support rendering markdown for notifications

* feat(notify): support markdown message for telegram & wework

* chore: remove deprecated notify wechat

* chore: use wechatpy instead of wechat3
  • Loading branch information
everpcpc authored Mar 8, 2021
1 parent e65ab8c commit 4dba546
Show file tree
Hide file tree
Showing 9 changed files with 318 additions and 195 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ For all the CLI tools, you can type `-h` or `--help` to get help messages and ex

### sa-notify

通知提醒工具,支持 wechat, wework(企业微信), email, sms, pushbullet, pushover, telegram 等多种通知类型。
通知提醒工具,支持 wework(企业微信), email, sms, pushbullet, pushover, telegram 等多种通知类型。

```shell
sa-notify --wechat user1 --content 'xxx'
echo 'xxx' | sa-notify --wechat user1,user2 --email [email protected] [email protected]
sa-notify --wework user1 --content 'xxx'
echo 'xxx' | sa-notify --wework user1,user2 --email [email protected] [email protected]
```

### sa-dns
Expand Down Expand Up @@ -150,9 +150,9 @@ icinga2 doc: <http://docs.icinga.org/icinga2/latest/doc/module/icinga2/toc>

```shell
# try test
sa-icinga notify --wechat user1 --email [email protected] --test
sa-icinga notify --wework user1 --email [email protected] --test

sa-icinga notify --wechat user1 --email [email protected] # need icinga pass os environment vars
sa-icinga notify --wework user1 --email [email protected] # need icinga pass os environment vars

sa-icinga ack --host sa --service check-puppet --comment 'hehe'
sa-icinga ack --host 'sa*' --service 'check-puppet'
Expand Down Expand Up @@ -197,7 +197,7 @@ sa-tc bm devices --alias host

### sa-github

`sa-github` 是对 Github API V3 的封装, 支持 github.com 和 ghe, 目前功能还在完善中, 只支持 collaborator api
`sa-github` 是对 Github API V3 的封装, 支持 github.com 和 ghe, 目前功能还在完善中, 只支持 collaborator api

```shell
sa-github collaborator --org xxx --repo yyy add --username user_1 --permission admin
Expand Down
146 changes: 83 additions & 63 deletions sa_tools_core/icinga.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from sa_tools_core.utils import get_os_username, AttrDict, import_string
from sa_tools_core.consts import ICINGA_EMAIL, ICINGA_CLUSTER_CONFIG_CLASS, ALERT_WIKI_BASE_URL

requests_logger = logging.getLogger('requests')
requests_logger = logging.getLogger("requests")

logger = logging.getLogger(__name__)

Expand All @@ -35,10 +35,10 @@ def show(args):
icinga_api = get_icinga_api(icinga_cluster_config)
params = {}
if args.attrs:
params['attrs'] = args.attrs
params["attrs"] = args.attrs
if args.filter:
params['filter'] = args.filter
res = (icinga_api.api.objects.url(inflect_engine.plural(args.type)).get(**params))
params["filter"] = args.filter
res = icinga_api.api.objects.url(inflect_engine.plural(args.type)).get(**params)
if args.raw:
print(res)
else:
Expand All @@ -49,33 +49,42 @@ def show(args):
def ack(args):
icinga_api = get_icinga_api(icinga_cluster_config)
ret, msg = icinga_api.acknowledge(
args.host, service=args.service, author=args.user, comment=args.comment, remove=args.remove, notify=args.notify)
args.host,
service=args.service,
author=args.user,
comment=args.comment,
remove=args.remove,
notify=args.notify,
)
if ret:
logger.info('icinga acknowledge %s/%s success: %s', args.host, args.service or '', msg)
logger.info("icinga acknowledge %s/%s success: %s", args.host, args.service or "", msg)
else:
logger.error('icinga acknowledge %s/%s failed: %s', args.host, args.service or '', msg)
logger.error("icinga acknowledge %s/%s failed: %s", args.host, args.service or "", msg)


@send_sentry
@require_user
def notify(args):
notifier = Notifier(from_addr=ICINGA_EMAIL)
notifier = Notifier(from_addr=ICINGA_EMAIL, msg_type="markdown")
env = (
dict(
TARGET_TYPE='service',
NAGIOS_LONGDATETIME='2016-05-11 16:30:50 +8000',
NAGIOS_NOTIFICATIONTYPE='PROBLEM',
NAGIOS_HOSTALIAS='sa',
NAGIOS_SERVICEDESC='fakeservice',
TARGET_TYPE="service",
NAGIOS_LONGDATETIME="2016-05-11 16:30:50 +8000",
NAGIOS_NOTIFICATIONTYPE="PROBLEM",
NAGIOS_HOSTALIAS="sa",
NAGIOS_SERVICEDESC="fakeservice",
NAGIOS_SERVICEOUTPUT="整个中文试试",
NAGIOS_SERVICESTATE='CRITICAL',
NOTIFICATIONAUTHORNAME='sysadmin',
NOTIFICATIONCOMMENT='没病走两步~',
NAGIOS_SERVICESTATE="CRITICAL",
NOTIFICATIONAUTHORNAME="sysadmin",
NOTIFICATIONCOMMENT="没病走两步~",
NOTIFICATION_IS_ARCHIVE=False,
NAGIOS_CONTACTNAME='shuaisa',
SERVICE_DURATION_SEC='5.001102',
NAGIOS_CUSTOM_WIKI=''
) if args.test else os.environ)
NAGIOS_CONTACTNAME="shuaisa",
SERVICE_DURATION_SEC="5.001102",
NAGIOS_CUSTOM_WIKI="https://wiki.example.com/service/fakeservice",
)
if args.test
else os.environ
)

unicode_env = {}
for name, value in env.items():
Expand All @@ -84,67 +93,74 @@ def notify(args):
else:
unicode_env[six.ensure_text(name)] = value

env = AttrDict(unicode_env, _default_value=six.u(''))
env = AttrDict(unicode_env, _default_value=six.u(""))

short_env = dict(
type=env.NAGIOS_NOTIFICATIONTYPE[:3].upper(),
host=env.NAGIOS_HOSTALIAS,
hoststate=env.HOSTSTATE,
service=env.NAGIOS_SERVICEDESC,
time=' '.join(env.NAGIOS_LONGDATETIME.split()[:2]),
extra=(env.NAGIOS_HOSTOUTPUT if env.TARGET_TYPE == 'host' else env.NAGIOS_SERVICEOUTPUT),
link='',
time=" ".join(env.NAGIOS_LONGDATETIME.split()[:2]),
extra=(env.NAGIOS_HOSTOUTPUT if env.TARGET_TYPE == "host" else env.NAGIOS_SERVICEOUTPUT),
link="",
custom_wiki_url=env.NAGIOS_CUSTOM_WIKI,
wiki_base_url=ALERT_WIKI_BASE_URL.rstrip(' /')
)
duration = env.SERVICE_DURATION_SEC if env.TARGET_TYPE == 'service' \
else env.HOST_DURATION_SEC
wiki_base_url=ALERT_WIKI_BASE_URL.rstrip(" /"),
)
duration = env.SERVICE_DURATION_SEC if env.TARGET_TYPE == "service" else env.HOST_DURATION_SEC

short_env = AttrDict(short_env, _default_value='')
short_env = AttrDict(short_env, _default_value="")
ack_link = icinga_cluster_config.get_ack_link(env)
reboot_host_link = icinga_cluster_config.get_reboot_host_link(env)
icinga_link = icinga_cluster_config.get_icinga_link(env)
for type_ in NOTIFY_TYPES:
values = vars(args)[type_]
if values:
addrs = [i for v in values for i in re.split(r'[,\s]+', v)]
addrs = [i for v in values for i in re.split(r"[,\s]+", v)]
addrs = [a for a in addrs if a]
if not addrs:
logger.warning('ignore empty %s addrs' % type_)
logger.warning("ignore empty %s addrs" % type_)
continue
title, content = render_notification(
env=env,
short_env=short_env,
notify_type=type_,
ack_link=ack_link,
reboot_host_link=reboot_host_link,
icinga_link=icinga_link)
icinga_link=icinga_link,
)
try:
ok = add_notification(
env.NAGIOS_NOTIFICATIONTYPE, short_env.host, short_env.hoststate, short_env.service, content, type_,
', '.join(addrs), duration)
logger.info('notification gateway permit: %s', ok)
env.NAGIOS_NOTIFICATIONTYPE,
short_env.host,
short_env.hoststate,
short_env.service,
content,
type_,
", ".join(addrs),
duration,
)
logger.info("notification gateway permit: %s", ok)
except Exception:
# we catch the exception and send it to sentry, but let the program continue to run
report()
logger.exception('add notification to gateway failed: ')
logger.exception("add notification to gateway failed: ")
ok = True
if ok:
try:
getattr(notifier, type_)(addrs, title=title, content=content)
except Exception as e:
report()
logger.error('Notifier.%s(%s) failed: %s', type_, addrs, e)
logger.error("Notifier.%s(%s) failed: %s", type_, addrs, e)


def main():
"""
e.g.
# try test
$ sa-icinga notify --wechat lihan --email [email protected] --test
$ sa-icinga notify --wework lihan --email [email protected] --test
# need icinga pass os environment vars
$ sa-icinga notify --wechat lihan --email [email protected]
$ sa-icinga notify --wework lihan --email [email protected]
# icinga2 doc: http://docs.icinga.org/icinga2/latest/doc/module/icinga2/toc
Expand All @@ -158,47 +174,51 @@ def main():
$ sa-icinga show --type user | grep lihan
$ sa-icinga show --filter 'service.name == "check-puppet"' --attrs acknowledgement
"""
parser = argparse.ArgumentParser(epilog=main.__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-u', '--user', help='LDAP username, use your OS user name by default.')
parser.add_argument('-v', '--verbose', help='Show more infomation.', action='store_true')
parser = argparse.ArgumentParser(
epilog=main.__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("-u", "--user", help="LDAP username, use your OS user name by default.")
parser.add_argument("-v", "--verbose", help="Show more infomation.", action="store_true")

# compatible with py2 & 3
subparsers = parser.add_subparsers(help='Sub commands', dest='subparser')
subparsers = parser.add_subparsers(help="Sub commands", dest="subparser")
subparsers.required = True

notify_parser = subparsers.add_parser('notify', help='Notify.')
notify_parser.add_argument('--test', action='store_true', help='test')
notify_parser = subparsers.add_parser("notify", help="Notify.")
notify_parser.add_argument("--test", action="store_true", help="test")
for type_ in NOTIFY_TYPES:
notify_parser.add_argument('--%s' % type_, nargs='*', help='your enterprise address of %s.' % type_)
notify_parser.add_argument(
"--%s" % type_, nargs="*", help="your enterprise address of %s." % type_
)
notify_parser.set_defaults(func=notify)
notify_parser.set_defaults(parser_name='notify')

ack_parser = subparsers.add_parser('ack', help='Acknowledge service or host problem.')
ack_parser.add_argument('--host', '-H', help='host', required=True)
ack_parser.add_argument('--service', '-s', help='service')
ack_parser.add_argument('--comment', '-c', help='comment', default='ack by sa-icinga')
ack_parser.add_argument('--notify', help='whether notify', default=False, action='store_true')
ack_parser.add_argument('--remove', help='remove ack', action='store_true')
notify_parser.set_defaults(parser_name="notify")

ack_parser = subparsers.add_parser("ack", help="Acknowledge service or host problem.")
ack_parser.add_argument("--host", "-H", help="host", required=True)
ack_parser.add_argument("--service", "-s", help="service")
ack_parser.add_argument("--comment", "-c", help="comment", default="ack by sa-icinga")
ack_parser.add_argument("--notify", help="whether notify", default=False, action="store_true")
ack_parser.add_argument("--remove", help="remove ack", action="store_true")
ack_parser.set_defaults(func=ack)
ack_parser.set_defaults(parser_name='ack')
ack_parser.set_defaults(parser_name="ack")

show_parser = subparsers.add_parser('show', help='Show objects.')
show_parser.add_argument('--type', '-t', help='type of objects', default='service')
show_parser.add_argument('--filter', '-f', help='filter')
show_parser.add_argument('--attrs', '-a', help='attrs of the objects to show', nargs='*')
show_parser.add_argument('--raw', '-r', help='ouput raw json data', action='store_true')
show_parser = subparsers.add_parser("show", help="Show objects.")
show_parser.add_argument("--type", "-t", help="type of objects", default="service")
show_parser.add_argument("--filter", "-f", help="filter")
show_parser.add_argument("--attrs", "-a", help="attrs of the objects to show", nargs="*")
show_parser.add_argument("--raw", "-r", help="ouput raw json data", action="store_true")
show_parser.set_defaults(func=show)
show_parser.set_defaults(parser_name='show')
show_parser.set_defaults(parser_name="show")

args = parser.parse_args()
args.user = args.user or get_os_username()

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(name)s %(levelname)s %(message)s')
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(name)s %(levelname)s %(message)s")

if not args.verbose:
requests_logger.setLevel(logging.ERROR)

if args.parser_name == 'notify':
if args.parser_name == "notify":
if not any(vars(args)[type_] for type_ in NOTIFY_TYPES):
sys.exit(0)

Expand Down
16 changes: 10 additions & 6 deletions sa_tools_core/libs/notify/telegram.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@
logger = logging.getLogger(__name__)


def send_message(chatid, content):
PUSH_KEY = get_config('telegram')
PUSH_URL = 'https://api.telegram.org/bot%s/sendMessage' % PUSH_KEY
def send_message(chatid, content, msg_type="text"):
PUSH_KEY = get_config("telegram")
PUSH_URL = "https://api.telegram.org/bot%s/sendMessage" % PUSH_KEY

message = {
"chat_id": chatid,
"text": content
"text": content,
}
if msg_type == "markdown":
message["parse_mode"] = "Markdown"
elif msg_type == "html":
message["parse_mode"] = "HTML"

req = requests.post(PUSH_URL, json=message, proxies=PROXIES)
resp = req.json()
if not resp['ok']:
raise Exception('api failed, resp: %s' % resp)
if not resp["ok"]:
raise Exception("api failed, resp: %s" % resp)
51 changes: 0 additions & 51 deletions sa_tools_core/libs/notify/wechat.py

This file was deleted.

Loading

0 comments on commit 4dba546

Please sign in to comment.