mirror of
https://github.com/valitydev/salt.git
synced 2024-11-06 08:35:21 +00:00
Merge pull request #51949 from mchugh19/remove_hipchat
Remove hipchat engine due to service retirement
This commit is contained in:
parent
8ce2511010
commit
5958b7e973
@ -11,7 +11,6 @@ engine modules
|
||||
:template: autosummary.rst.tmpl
|
||||
|
||||
docker_events
|
||||
hipchat
|
||||
http_logstash
|
||||
ircbot
|
||||
junos_syslog
|
||||
|
@ -1,6 +0,0 @@
|
||||
salt.engines.hipchat module
|
||||
===========================
|
||||
|
||||
.. automodule:: salt.engines.hipchat
|
||||
:members:
|
||||
:undoc-members:
|
@ -1,429 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
An engine that reads messages from Hipchat and sends them to the Salt
|
||||
event bus. Alternatively Salt commands can be sent to the Salt master
|
||||
via Hipchat by setting the control parameter to ``True`` and using command
|
||||
prefaced with a ``!``. Only token key is required, but room and control
|
||||
keys make the engine interactive.
|
||||
|
||||
.. versionadded: 2016.11.0
|
||||
|
||||
:depends: hypchat
|
||||
:configuration: Example configuration
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
engines:
|
||||
- hipchat:
|
||||
api_url: http://api.hipchat.myteam.com
|
||||
token: 'XXXXXX'
|
||||
room: 'salt'
|
||||
control: True
|
||||
valid_users:
|
||||
- SomeUser
|
||||
valid_commands:
|
||||
- test.ping
|
||||
- cmd.run
|
||||
- list_jobs
|
||||
- list_commands
|
||||
aliases:
|
||||
list_jobs:
|
||||
cmd: jobs.list_jobs
|
||||
list_commands:
|
||||
cmd: pillar.get salt:engines:hipchat:valid_commands target=saltmaster
|
||||
max_rooms: 0
|
||||
wait_time: 1
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import logging
|
||||
import time
|
||||
import os
|
||||
|
||||
|
||||
try:
|
||||
import hypchat
|
||||
except ImportError:
|
||||
hypchat = None
|
||||
|
||||
import salt.utils.args
|
||||
import salt.utils.event
|
||||
import salt.utils.files
|
||||
import salt.utils.http
|
||||
import salt.utils.json
|
||||
import salt.utils.stringutils
|
||||
import salt.runner
|
||||
import salt.client
|
||||
import salt.loader
|
||||
import salt.output
|
||||
from salt.ext import six
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_DEFAULT_API_URL = 'https://api.hipchat.com'
|
||||
_DEFAULT_SLEEP = 5
|
||||
_DEFAULT_MAX_ROOMS = 1000
|
||||
|
||||
__virtualname__ = 'hipchat'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
return __virtualname__ if hypchat is not None \
|
||||
else (False, 'hypchat is not installed')
|
||||
|
||||
|
||||
def _publish_file(token, room, filepath, message='', outputter=None, api_url=None):
|
||||
'''
|
||||
Send file to a HipChat room via API version 2
|
||||
|
||||
Parameters
|
||||
----------
|
||||
token : str
|
||||
HipChat API version 2 compatible token - must be token for active user
|
||||
room: str
|
||||
Name or API ID of the room to notify
|
||||
filepath: str
|
||||
Full path of file to be sent
|
||||
message: str, optional
|
||||
Message to send to room
|
||||
api_url: str, optional
|
||||
Hipchat API URL to use, defaults to http://api.hipchat.com
|
||||
'''
|
||||
|
||||
if not os.path.isfile(filepath):
|
||||
raise ValueError("File '{0}' does not exist".format(filepath))
|
||||
if len(message) > 1000:
|
||||
raise ValueError('Message too long')
|
||||
|
||||
url = "{0}/v2/room/{1}/share/file".format(api_url, room)
|
||||
headers = {'Content-type': 'multipart/related; boundary=boundary123456'}
|
||||
headers['Authorization'] = "Bearer " + token
|
||||
msg = salt.utils.json.dumps({'message': message})
|
||||
|
||||
# future lint: disable=blacklisted-function
|
||||
with salt.utils.files.fopen(filepath, 'rb') as rfh:
|
||||
payload = str('''\
|
||||
--boundary123456
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
Content-Disposition: attachment; name="metadata"
|
||||
|
||||
{0}
|
||||
|
||||
--boundary123456
|
||||
Content-Disposition: attachment; name="file"; filename="{1}"
|
||||
|
||||
{2}
|
||||
|
||||
--boundary123456--\
|
||||
''').format(msg,
|
||||
os.path.basename(salt.utils.stringutils.to_str(filepath)),
|
||||
salt.utils.stringutils.to_str(rfh.read()))
|
||||
# future lint: enable=blacklisted-function
|
||||
|
||||
salt.utils.http.query(url, method='POST', header_dict=headers, data=payload)
|
||||
|
||||
|
||||
def _publish_html_message(token, room, data, message='', outputter='nested', api_url=None):
|
||||
'''
|
||||
Publishes the HTML-formatted message.
|
||||
'''
|
||||
url = "{0}/v2/room/{1}/notification".format(api_url, room)
|
||||
headers = {
|
||||
'Content-type': 'text/plain'
|
||||
}
|
||||
headers['Authorization'] = 'Bearer ' + token
|
||||
salt.utils.http.query(
|
||||
url,
|
||||
'POST',
|
||||
data=message,
|
||||
decode=True,
|
||||
status=True,
|
||||
header_dict=headers,
|
||||
opts=__opts__,
|
||||
)
|
||||
headers['Content-type'] = 'text/html'
|
||||
message = salt.output.html_format(data, outputter, opts=__opts__)
|
||||
salt.utils.http.query(
|
||||
url,
|
||||
'POST',
|
||||
data=message,
|
||||
decode=True,
|
||||
status=True,
|
||||
header_dict=headers,
|
||||
opts=__opts__,
|
||||
)
|
||||
|
||||
|
||||
def _publish_code_message(token, room, data, message='', outputter='nested', api_url=None):
|
||||
'''
|
||||
Publishes the output format as code.
|
||||
'''
|
||||
url = "{0}/v2/room/{1}/notification".format(api_url, room)
|
||||
headers = {
|
||||
'Content-type': 'text/plain'
|
||||
}
|
||||
headers['Authorization'] = 'Bearer ' + token
|
||||
salt.utils.http.query(
|
||||
url,
|
||||
'POST',
|
||||
data=message,
|
||||
decode=True,
|
||||
status=True,
|
||||
header_dict=headers,
|
||||
opts=__opts__,
|
||||
)
|
||||
message = '/code '
|
||||
message += salt.output.string_format(data, outputter, opts=__opts__)
|
||||
salt.utils.http.query(
|
||||
url,
|
||||
'POST',
|
||||
data=message,
|
||||
decode=True,
|
||||
status=True,
|
||||
header_dict=headers,
|
||||
opts=__opts__,
|
||||
)
|
||||
|
||||
|
||||
def start(token,
|
||||
room='salt',
|
||||
aliases=None,
|
||||
valid_users=None,
|
||||
valid_commands=None,
|
||||
control=False,
|
||||
trigger="!",
|
||||
tag='salt/engines/hipchat/incoming',
|
||||
api_key=None,
|
||||
api_url=None,
|
||||
max_rooms=None,
|
||||
wait_time=None,
|
||||
output_type='file',
|
||||
outputter='nested'):
|
||||
'''
|
||||
Listen to Hipchat messages and forward them to Salt.
|
||||
|
||||
token
|
||||
The HipChat API key. It requires a key for global usgae,
|
||||
assigned per user, rather than room.
|
||||
|
||||
room
|
||||
The HipChat room name.
|
||||
|
||||
aliases
|
||||
Define custom aliases.
|
||||
|
||||
valid_users
|
||||
Restrict access only to certain users.
|
||||
|
||||
valid_commands
|
||||
Restrict the execution to a limited set of commands.
|
||||
|
||||
control
|
||||
Send commands to the master.
|
||||
|
||||
trigger: ``!``
|
||||
Special character that triggers the execution of salt commands.
|
||||
|
||||
tag: ``salt/engines/hipchat/incoming``
|
||||
The event tag on the Salt bus.
|
||||
|
||||
api_url: ``https://api.hipchat.com``
|
||||
The URL to the HipChat API.
|
||||
|
||||
.. versionadded:: 2017.7.0
|
||||
|
||||
max_rooms: ``1000``
|
||||
Maximum number of rooms allowed to fetch. If set to 0,
|
||||
it is able to retrieve the entire list of rooms.
|
||||
|
||||
wait_time: ``5``
|
||||
Maximum wait time, in seconds.
|
||||
|
||||
output_type: ``file``
|
||||
The type of the output. Choose bewteen:
|
||||
|
||||
- ``file``: save the output into a temporary file and upload
|
||||
- ``html``: send the output as HTML
|
||||
- ``code``: send the output as code
|
||||
|
||||
This can be overridden when executing a command, using the ``--out-type`` argument.
|
||||
|
||||
.. versionadded:: 2017.7.0
|
||||
|
||||
outputter: ``nested``
|
||||
The format to display the data, using the outputters available on the CLI.
|
||||
This argument can also be overridden when executing a command, using the ``--out`` option.
|
||||
|
||||
.. versionadded:: 2017.7.0
|
||||
|
||||
HipChat Example:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
! test.ping
|
||||
! test.ping target=minion1
|
||||
! test.ping --out=nested
|
||||
! test.ping --out-type=code --out=table
|
||||
'''
|
||||
target_room = None
|
||||
|
||||
if __opts__.get('__role') == 'master':
|
||||
fire_master = salt.utils.event.get_master_event(
|
||||
__opts__,
|
||||
__opts__['sock_dir']).fire_event
|
||||
else:
|
||||
fire_master = None
|
||||
|
||||
def fire(tag, msg):
|
||||
'''
|
||||
fire event to salt bus
|
||||
'''
|
||||
|
||||
if fire_master:
|
||||
fire_master(msg, tag)
|
||||
else:
|
||||
__salt__['event.send'](tag, msg)
|
||||
|
||||
def _eval_bot_mentions(all_messages, trigger):
|
||||
''' yield partner message '''
|
||||
for message in all_messages:
|
||||
message_text = message['message']
|
||||
if message_text.startswith(trigger):
|
||||
fire(tag, message)
|
||||
text = message_text.replace(trigger, '').strip()
|
||||
yield message['from']['mention_name'], text
|
||||
|
||||
token = token or api_key
|
||||
if not token:
|
||||
raise UserWarning("Hipchat token not found")
|
||||
|
||||
runner_functions = sorted(salt.runner.Runner(__opts__).functions)
|
||||
|
||||
if not api_url:
|
||||
api_url = _DEFAULT_API_URL
|
||||
hipc = hypchat.HypChat(token, endpoint=api_url)
|
||||
if not hipc:
|
||||
raise UserWarning("Unable to connect to hipchat")
|
||||
|
||||
log.debug('Connected to Hipchat')
|
||||
rooms_kwargs = {}
|
||||
if max_rooms is None:
|
||||
max_rooms = _DEFAULT_MAX_ROOMS
|
||||
rooms_kwargs['max_results'] = max_rooms
|
||||
elif max_rooms > 0:
|
||||
rooms_kwargs['max_results'] = max_rooms
|
||||
# if max_rooms is 0 => retrieve all (rooms_kwargs is empty dict)
|
||||
all_rooms = hipc.rooms(**rooms_kwargs)['items']
|
||||
for a_room in all_rooms:
|
||||
if a_room['name'] == room:
|
||||
target_room = a_room
|
||||
if not target_room:
|
||||
log.debug("Unable to connect to room %s", room)
|
||||
# wait for a bit as to not burn through api calls
|
||||
time.sleep(30)
|
||||
raise UserWarning("Unable to connect to room {0}".format(room))
|
||||
|
||||
after_message_id = target_room.latest(maxResults=1)['items'][0]['id']
|
||||
|
||||
while True:
|
||||
try:
|
||||
new_messages = target_room.latest(
|
||||
not_before=after_message_id)['items']
|
||||
except hypchat.requests.HttpServiceUnavailable:
|
||||
time.sleep(15)
|
||||
continue
|
||||
|
||||
after_message_id = new_messages[-1]['id']
|
||||
for partner, text in _eval_bot_mentions(new_messages[1:], trigger):
|
||||
# bot summoned by partner
|
||||
|
||||
if not control:
|
||||
log.debug("Engine not configured for control")
|
||||
return
|
||||
|
||||
# Ensure the user is allowed to run commands
|
||||
if valid_users:
|
||||
if partner not in valid_users:
|
||||
target_room.message('{0} not authorized to run Salt commands'.format(partner))
|
||||
return
|
||||
|
||||
args = []
|
||||
kwargs = {}
|
||||
|
||||
cmdline = salt.utils.args.shlex_split(text)
|
||||
cmd = cmdline[0]
|
||||
|
||||
# Evaluate aliases
|
||||
if aliases and isinstance(aliases, dict) and cmd in aliases.keys():
|
||||
cmdline = aliases[cmd].get('cmd')
|
||||
cmdline = salt.utils.args.shlex_split(cmdline)
|
||||
cmd = cmdline[0]
|
||||
|
||||
# Parse args and kwargs
|
||||
if len(cmdline) > 1:
|
||||
for item in cmdline[1:]:
|
||||
if '=' in item:
|
||||
(key, value) = item.split('=', 1)
|
||||
kwargs[key] = value
|
||||
else:
|
||||
args.append(item)
|
||||
|
||||
# Check for target. Otherwise assume *
|
||||
if 'target' not in kwargs:
|
||||
target = '*'
|
||||
else:
|
||||
target = kwargs['target']
|
||||
del kwargs['target']
|
||||
|
||||
# Check for tgt_type. Otherwise assume glob
|
||||
if 'tgt_type' not in kwargs:
|
||||
tgt_type = 'glob'
|
||||
else:
|
||||
tgt_type = kwargs['tgt_type']
|
||||
del kwargs['tgt_type']
|
||||
|
||||
# Check for outputter. Otherwise assume nested
|
||||
if '--out' in kwargs:
|
||||
outputter = kwargs['--out']
|
||||
del kwargs['--out']
|
||||
|
||||
# Check for outputter. Otherwise assume nested
|
||||
if '--out-type' in kwargs:
|
||||
output_type = kwargs['--out-type']
|
||||
del kwargs['--out-type']
|
||||
|
||||
# Ensure the command is allowed
|
||||
if valid_commands:
|
||||
if cmd not in valid_commands:
|
||||
target_room.message('Using {0} is not allowed.'.format(cmd))
|
||||
return
|
||||
|
||||
ret = {}
|
||||
if cmd in runner_functions:
|
||||
runner = salt.runner.RunnerClient(__opts__)
|
||||
ret = runner.cmd(cmd, arg=args, kwarg=kwargs)
|
||||
|
||||
# Default to trying to run as a client module.
|
||||
else:
|
||||
local = salt.client.LocalClient()
|
||||
ret = local.cmd('{0}'.format(target), cmd, args, kwargs, tgt_type='{0}'.format(tgt_type))
|
||||
|
||||
nice_args = (' ' + ' '.join(args)) if args else ''
|
||||
nice_kwargs = (' ' + ' '.join('{0}={1}'.format(key, value) for (key, value) in six.iteritems(kwargs))) \
|
||||
if kwargs else ''
|
||||
message_string = '@{0} Results for: {1}{2}{3} on {4}'.format(partner,
|
||||
cmd,
|
||||
nice_args,
|
||||
nice_kwargs,
|
||||
target)
|
||||
if output_type == 'html':
|
||||
_publish_html_message(token, room, ret, message=message_string, outputter=outputter, api_url=api_url)
|
||||
elif output_type == 'code':
|
||||
_publish_code_message(token, room, ret, message=message_string, outputter=outputter, api_url=api_url)
|
||||
else:
|
||||
tmp_path_fn = salt.utils.files.mkstemp()
|
||||
with salt.utils.files.fopen(tmp_path_fn, 'w+') as fp_:
|
||||
salt.utils.json.dump(ret, fp_, sort_keys=True, indent=4)
|
||||
_publish_file(token, room, tmp_path_fn, message=message_string, api_url=api_url)
|
||||
salt.utils.files.safe_rm(tmp_path_fn)
|
||||
time.sleep(wait_time or _DEFAULT_SLEEP)
|
Loading…
Reference in New Issue
Block a user