From 5958b7e973c83012ba4a8fd235d0322359adb488 Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Tue, 5 Mar 2019 08:03:35 -0800 Subject: [PATCH 1/2] Merge pull request #51949 from mchugh19/remove_hipchat Remove hipchat engine due to service retirement --- doc/ref/engines/all/index.rst | 1 - doc/ref/engines/all/salt.engines.hipchat.rst | 6 - salt/engines/hipchat.py | 429 ------------------- 3 files changed, 436 deletions(-) delete mode 100644 doc/ref/engines/all/salt.engines.hipchat.rst delete mode 100644 salt/engines/hipchat.py diff --git a/doc/ref/engines/all/index.rst b/doc/ref/engines/all/index.rst index 15f6d8a667e0..d9b19e016666 100644 --- a/doc/ref/engines/all/index.rst +++ b/doc/ref/engines/all/index.rst @@ -11,7 +11,6 @@ engine modules :template: autosummary.rst.tmpl docker_events - hipchat http_logstash ircbot junos_syslog diff --git a/doc/ref/engines/all/salt.engines.hipchat.rst b/doc/ref/engines/all/salt.engines.hipchat.rst deleted file mode 100644 index 4b12b40f99b2..000000000000 --- a/doc/ref/engines/all/salt.engines.hipchat.rst +++ /dev/null @@ -1,6 +0,0 @@ -salt.engines.hipchat module -=========================== - -.. automodule:: salt.engines.hipchat - :members: - :undoc-members: diff --git a/salt/engines/hipchat.py b/salt/engines/hipchat.py deleted file mode 100644 index d25e428e72ea..000000000000 --- a/salt/engines/hipchat.py +++ /dev/null @@ -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) From 0c251cf9192ba708b72d61d79bd796bc50345ca7 Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Sat, 16 Mar 2019 08:35:21 -0700 Subject: [PATCH 2/2] Merge pull request #51996 from mchugh19/remove_hipchat Add release note about hipchat removal --- doc/ref/modules/all/index.rst | 1 - doc/ref/modules/all/salt.modules.hipchat.rst | 6 - doc/ref/returners/all/index.rst | 1 - .../all/salt.returners.hipchat_return.rst | 6 - doc/ref/states/all/index.rst | 1 - doc/ref/states/all/salt.states.hipchat.rst | 6 - doc/topics/releases/neon.rst | 32 ++ salt/modules/hipchat.py | 356 ---------------- salt/returners/hipchat_return.py | 400 ------------------ salt/states/hipchat.py | 144 ------- tests/unit/modules/test_hipchat.py | 93 ---- tests/unit/states/test_hipchat.py | 74 ---- 12 files changed, 32 insertions(+), 1088 deletions(-) delete mode 100644 doc/ref/modules/all/salt.modules.hipchat.rst delete mode 100644 doc/ref/returners/all/salt.returners.hipchat_return.rst delete mode 100644 doc/ref/states/all/salt.states.hipchat.rst delete mode 100644 salt/modules/hipchat.py delete mode 100644 salt/returners/hipchat_return.py delete mode 100644 salt/states/hipchat.py delete mode 100644 tests/unit/modules/test_hipchat.py delete mode 100644 tests/unit/states/test_hipchat.py diff --git a/doc/ref/modules/all/index.rst b/doc/ref/modules/all/index.rst index 94a42d8dad80..4fb32ea9b4c7 100644 --- a/doc/ref/modules/all/index.rst +++ b/doc/ref/modules/all/index.rst @@ -171,7 +171,6 @@ execution modules hashutil heat hg - hipchat hosts http ifttt diff --git a/doc/ref/modules/all/salt.modules.hipchat.rst b/doc/ref/modules/all/salt.modules.hipchat.rst deleted file mode 100644 index 09b519a3a93a..000000000000 --- a/doc/ref/modules/all/salt.modules.hipchat.rst +++ /dev/null @@ -1,6 +0,0 @@ -==================== -salt.modules.hipchat -==================== - -.. automodule:: salt.modules.hipchat - :members: diff --git a/doc/ref/returners/all/index.rst b/doc/ref/returners/all/index.rst index 9ee7a3b0b3d0..68831349e34c 100644 --- a/doc/ref/returners/all/index.rst +++ b/doc/ref/returners/all/index.rst @@ -19,7 +19,6 @@ returner modules elasticsearch_return etcd_return highstate_return - hipchat_return influxdb_return kafka_return librato_return diff --git a/doc/ref/returners/all/salt.returners.hipchat_return.rst b/doc/ref/returners/all/salt.returners.hipchat_return.rst deleted file mode 100644 index 37e5d6d4be11..000000000000 --- a/doc/ref/returners/all/salt.returners.hipchat_return.rst +++ /dev/null @@ -1,6 +0,0 @@ -============================= -salt.returners.hipchat_return -============================= - -.. automodule:: salt.returners.hipchat_return - :members: diff --git a/doc/ref/states/all/index.rst b/doc/ref/states/all/index.rst index fb9ac25953a0..292a728a0614 100644 --- a/doc/ref/states/all/index.rst +++ b/doc/ref/states/all/index.rst @@ -115,7 +115,6 @@ state modules group heat hg - hipchat host http icinga2 diff --git a/doc/ref/states/all/salt.states.hipchat.rst b/doc/ref/states/all/salt.states.hipchat.rst deleted file mode 100644 index 921bd1bbe89a..000000000000 --- a/doc/ref/states/all/salt.states.hipchat.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================== -salt.states.hipchat -=================== - -.. automodule:: salt.states.hipchat - :members: diff --git a/doc/topics/releases/neon.rst b/doc/topics/releases/neon.rst index 780a8d9e429b..3b250266d8d5 100644 --- a/doc/topics/releases/neon.rst +++ b/doc/topics/releases/neon.rst @@ -3,3 +3,35 @@ ================================== Salt Release Notes - Codename Neon ================================== + +Deprecations +============ + +Module Deprecations +------------------- + +- The hipchat module has been removed due to the service being retired. + :py:func:`Google Chat `, + :py:func:`MS Teams `, or + :py:func:`Slack ` may be suitable replacements. + + +State Deprecations +------------------ + +- The hipchat state has been removed due to the service being retired. + :py:func:`MS Teams ` or + :py:func:`Slack ` may be suitable replacements. + +Engine Removal +-------------- + +- The hipchat engine has been removed due to the service being retired. For users migrating + to Slack, the :py:func:`slack ` engine may be a suitable replacement. + +Returner Removal +---------------- + +- The hipchat returner has been removed due to the service being retired. For users migrating + to Slack, the :py:func:`slack ` returner may be a suitable + replacement. diff --git a/salt/modules/hipchat.py b/salt/modules/hipchat.py deleted file mode 100644 index db7e5891279e..000000000000 --- a/salt/modules/hipchat.py +++ /dev/null @@ -1,356 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Module for sending messages to hipchat. - -.. versionadded:: 2015.5.0 - -:configuration: This module can be used by either passing an api key and version - directly or by specifying both in a configuration profile in the salt - master/minion config. - - It is possible to use a different API than http://api.hipchat.com, - by specifying the API URL in config as api_url, or by passing the value directly. - - For example: - - .. code-block:: yaml - - hipchat: - api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - api_version: v1 - - Custom API Example: - - .. code-block:: yaml - - hipchat: - api_url: http://api.hipchat.myteam.com - api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - api_version: v2 -''' -# Import Python Libs -from __future__ import absolute_import, print_function, unicode_literals -import logging - -# Import Salt libs -import salt.utils.http -import salt.utils.json - -# Import 3rd-party Libs -# pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext.six.moves.urllib.parse import urljoin as _urljoin -from salt.ext.six.moves.urllib.parse import urlencode as _urlencode -from salt.ext.six.moves import range -import salt.ext.six.moves.http_client - -# pylint: enable=import-error,no-name-in-module,redefined-builtin - -log = logging.getLogger(__name__) - -__virtualname__ = 'hipchat' - - -def __virtual__(): - ''' - Return virtual name of the module. - - :return: The virtual name of the module. - ''' - return __virtualname__ - - -def _query(function, - api_url=None, - api_key=None, - api_version=None, - room_id=None, - method='GET', - data=None): - ''' - HipChat object method function to construct and execute on the API URL. - - :param api_url: The HipChat API URL. - :param api_key: The HipChat api key. - :param function: The HipChat api function to perform. - :param api_version: The HipChat api version (v1 or v2). - :param method: The HTTP method, e.g. GET or POST. - :param data: The data to be sent for POST method. - :return: The json response from the API call or False. - ''' - headers = {} - query_params = {} - - if not api_url: - try: - options = __salt__['config.option']('hipchat') - api_url = options.get('api_url') - except (NameError, KeyError, AttributeError): - pass # not mandatory, thus won't fail if not found - - if not api_key or not api_version: - try: - options = __salt__['config.option']('hipchat') - if not api_key: - api_key = options.get('api_key') - if not api_version: - api_version = options.get('api_version') - except (NameError, KeyError, AttributeError): - log.error("No HipChat api key or version found.") - return False - - if room_id: - room_id = 'room/{0}/notification'.format(room_id) - else: - room_id = 'room/0/notification' - - hipchat_functions = { - 'v1': { - 'rooms': { - 'request': 'rooms/list', - 'response': 'rooms', - }, - 'users': { - 'request': 'users/list', - 'response': 'users', - }, - 'message': { - 'request': 'rooms/message', - 'response': 'status', - }, - }, - 'v2': { - 'rooms': { - 'request': 'room', - 'response': 'items', - }, - 'users': { - 'request': 'user', - 'response': 'items', - }, - 'message': { - 'request': room_id, - 'response': None, - }, - }, - } - - use_api_url = 'https://api.hipchat.com' # default API URL - if api_url: - use_api_url = api_url - base_url = _urljoin(use_api_url, api_version + '/') - path = hipchat_functions.get(api_version).get(function).get('request') - url = _urljoin(base_url, path, False) - - if api_version == 'v1': - query_params['format'] = 'json' - query_params['auth_token'] = api_key - - if method == 'POST': - headers['Content-Type'] = 'application/x-www-form-urlencoded' - - if data: - if data.get('notify', None): - data['notify'] = 1 - data = _urlencode(data) - elif api_version == 'v2': - headers['Authorization'] = 'Bearer {0}'.format(api_key) - if data: - data = salt.utils.json.dumps(data) - - if method == 'POST': - headers['Content-Type'] = 'application/json' - else: - log.error('Unsupported HipChat API version') - return False - - result = salt.utils.http.query( - url, - method, - params=query_params, - data=data, - decode=True, - status=True, - header_dict=headers, - opts=__opts__, - ) - - if result.get('status', None) == salt.ext.six.moves.http_client.OK: - response = hipchat_functions.get(api_version).get(function).get('response') - return result.get('dict', {}).get(response, None) - elif result.get('status', None) == salt.ext.six.moves.http_client.NO_CONTENT and \ - api_version == 'v2': - return True - else: - log.debug(url) - log.debug(query_params) - log.debug(data) - log.debug(result) - if result.get('error'): - log.error(result) - return False - - -def list_rooms(api_url=None, - api_key=None, - api_version=None): - ''' - List all HipChat rooms. - - :param api_url: The HipChat API URL, if not specified in the configuration. - :param api_key: The HipChat admin api key. - :param api_version: The HipChat api version, if not specified in the configuration. - :return: The room list. - - CLI Example: - - .. code-block:: bash - - salt '*' hipchat.list_rooms - - salt '*' hipchat.list_rooms api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_version=v1 - ''' - foo = _query(function='rooms', - api_url=api_url, - api_key=api_key, - api_version=api_version) - log.debug('foo %s', foo) - return foo - - -def list_users(api_url=None, - api_key=None, - api_version=None): - ''' - List all HipChat users. - - :param api_url: The HipChat API URL, if not specified in the configuration. - :param api_key: The HipChat admin api key. - :param api_version: The HipChat api version, if not specified in the configuration. - :return: The user list. - - CLI Example: - - .. code-block:: bash - - salt '*' hipchat.list_users - - salt '*' hipchat.list_users api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_version=v1 - ''' - return _query(function='users', - api_url=api_url, - api_key=api_key, - api_version=api_version) - - -def find_room(name, - api_url=None, - api_key=None, - api_version=None): - ''' - Find a room by name and return it. - - :param name: The room name. - :param api_url: The HipChat API URL, if not specified in the configuration. - :param api_key: The HipChat admin api key. - :param api_version: The HipChat api version, if not specified in the configuration. - :return: The room object. - - CLI Example: - - .. code-block:: bash - - salt '*' hipchat.find_room name="Development Room" - - salt '*' hipchat.find_room name="Development Room" api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_version=v1 - ''' - rooms = list_rooms(api_url=api_url, - api_key=api_key, - api_version=api_version) - if rooms: - for x in range(0, len(rooms)): - if rooms[x]['name'] == name: - return rooms[x] - return False - - -def find_user(name, - api_url=None, - api_key=None, - api_version=None): - ''' - Find a user by name and return it. - - :param name: The user name. - :param api_url: The HipChat API URL, if not specified in the configuration. - :param api_key: The HipChat admin api key. - :param api_version: The HipChat api version, if not specified in the configuration. - :return: The user object. - - CLI Example: - - .. code-block:: bash - - salt '*' hipchat.find_user name="Thomas Hatch" - - salt '*' hipchat.find_user name="Thomas Hatch" api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_version=v1 - ''' - users = list_users(api_url=api_url, - api_key=api_key, - api_version=api_version) - if users: - for x in range(0, len(users)): - if users[x]['name'] == name: - return users[x] - return False - - -def send_message(room_id, - message, - from_name, - api_url=None, - api_key=None, - api_version=None, - color='yellow', - notify=False): - ''' - Send a message to a HipChat room. - - :param room_id: The room id or room name, either will work. - :param message: The message to send to the HipChat room. - :param from_name: Specify who the message is from. - :param api_url: The HipChat api URL, if not specified in the configuration. - :param api_key: The HipChat api key, if not specified in the configuration. - :param api_version: The HipChat api version, if not specified in the configuration. - :param color: The color for the message, default: yellow. - :param notify: Whether to notify the room, default: False. - :return: Boolean if message was sent successfully. - - CLI Example: - - .. code-block:: bash - - salt '*' hipchat.send_message room_id="Development Room" message="Build is done" from_name="Build Server" - - salt '*' hipchat.send_message room_id="Development Room" message="Build failed" from_name="Build Server" color="red" notify=True - ''' - - parameters = dict() - parameters['room_id'] = room_id - parameters['from'] = from_name[:15] - parameters['message'] = message[:10000] - parameters['message_format'] = 'text' - parameters['color'] = color - parameters['notify'] = notify - - result = _query(function='message', - api_url=api_url, - api_key=api_key, - api_version=api_version, - room_id=room_id, - method='POST', - data=parameters) - - if result: - return True - else: - return False diff --git a/salt/returners/hipchat_return.py b/salt/returners/hipchat_return.py deleted file mode 100644 index a777a7322803..000000000000 --- a/salt/returners/hipchat_return.py +++ /dev/null @@ -1,400 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Return salt data via hipchat. - -.. versionadded:: 2015.5.0 - -The following fields can be set in the minion conf file:: - - hipchat.room_id (required) - hipchat.api_key (required) - hipchat.api_version (required) - hipchat.api_url (optional) - hipchat.from_name (required) - hipchat.color (optional) - hipchat.notify (optional) - hipchat.profile (optional) - hipchat.url (optional) - -.. note:: - - When using Hipchat's API v2, ``api_key`` needs to be assigned to the room with the - "Label" set to what you would have been set in the hipchat.from_name field. The v2 - API disregards the ``from_name`` in the data sent for the room notification and uses - the Label assigned through the Hipchat control panel. - -Alternative configuration values can be used by prefacing the configuration. -Any values not found in the alternative configuration will be pulled from -the default location:: - - hipchat.room_id - hipchat.api_key - hipchat.api_version - hipchat.api_url - hipchat.from_name - -Hipchat settings may also be configured as: - -.. code-block:: yaml - - hipchat: - room_id: RoomName - api_url: https://hipchat.myteam.con - api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - api_version: v1 - from_name: user@email.com - - alternative.hipchat: - room_id: RoomName - api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - api_version: v1 - from_name: user@email.com - - hipchat_profile: - hipchat.api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - hipchat.api_version: v1 - hipchat.from_name: user@email.com - - hipchat: - profile: hipchat_profile - room_id: RoomName - - alternative.hipchat: - profile: hipchat_profile - room_id: RoomName - - hipchat: - room_id: RoomName - api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - api_version: v1 - api_url: api.hipchat.com - from_name: user@email.com - -To use the HipChat returner, append '--return hipchat' to the salt command. - -.. code-block:: bash - - salt '*' test.ping --return hipchat - -To use the alternative configuration, append '--return_config alternative' to the salt command. - -.. versionadded:: 2015.5.0 - -.. code-block:: bash - - salt '*' test.ping --return hipchat --return_config alternative - -To override individual configuration items, append --return_kwargs '{"key:": "value"}' to the salt command. - -.. versionadded:: 2016.3.0 - -.. code-block:: bash - - salt '*' test.ping --return hipchat --return_kwargs '{"room_id": "another-room"}' - -''' -from __future__ import absolute_import, print_function, unicode_literals - -# Import Python libs -import pprint -import logging - -# pylint: disable=import-error,no-name-in-module -from salt.ext import six -from salt.ext.six.moves.urllib.parse import urljoin as _urljoin -from salt.ext.six.moves.urllib.parse import urlencode as _urlencode -import salt.ext.six.moves.http_client -# pylint: enable=import-error - -# Import Salt Libs -import salt.returners -import salt.utils.json - - -log = logging.getLogger(__name__) -__virtualname__ = 'hipchat' - - -def _get_options(ret=None): - ''' - Get the hipchat options from salt. - ''' - - defaults = {'color': 'yellow', - 'notify': False, - 'api_url': 'api.hipchat.com'} - - attrs = {'hipchat_profile': 'profile', - 'room_id': 'room_id', - 'from_name': 'from_name', - 'api_key': 'api_key', - 'api_version': 'api_version', - 'color': 'color', - 'notify': 'notify', - 'api_url': 'api_url', - } - - profile_attr = 'hipchat_profile' - - profile_attrs = {'from_jid': 'from_jid', - 'api_key': 'api_key', - 'api_version': 'api_key', - 'api_url': 'api_url', - } - - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - profile_attr=profile_attr, - profile_attrs=profile_attrs, - __salt__=__salt__, - __opts__=__opts__, - defaults=defaults) - return _options - - -def __virtual__(): - ''' - Return virtual name of the module. - - :return: The virtual name of the module. - ''' - return __virtualname__ - - -def _query(function, - api_key=None, - api_version=None, - room_id=None, - api_url=None, - method='GET', - data=None): - ''' - HipChat object method function to construct and execute on the API URL. - - :param api_url: The HipChat API URL. - :param api_key: The HipChat api key. - :param function: The HipChat api function to perform. - :param api_version: The HipChat api version (v1 or v2). - :param method: The HTTP method, e.g. GET or POST. - :param data: The data to be sent for POST method. - :return: The json response from the API call or False. - ''' - headers = {} - query_params = {} - - if room_id: - room_id = 'room/{0}/notification'.format(six.text_type(room_id)) - else: - room_id = 'room/0/notification' - - hipchat_functions = { - 'v1': { - 'rooms': { - 'request': 'rooms/list', - 'response': 'rooms', - }, - 'users': { - 'request': 'users/list', - 'response': 'users', - }, - 'message': { - 'request': 'rooms/message', - 'response': 'status', - }, - }, - 'v2': { - 'rooms': { - 'request': 'room', - 'response': 'items', - }, - 'users': { - 'request': 'user', - 'response': 'items', - }, - 'message': { - 'request': room_id, - 'response': None, - }, - }, - } - - api_url = 'https://{0}'.format(api_url) - base_url = _urljoin(api_url, api_version + '/') - path = hipchat_functions.get(api_version).get(function).get('request') - url = _urljoin(base_url, path, False) - - if api_version == 'v1': - query_params['format'] = 'json' - query_params['auth_token'] = api_key - - if method == 'POST': - headers['Content-Type'] = 'application/x-www-form-urlencoded' - - if data: - if data.get('notify'): - data['notify'] = 1 - else: - data['notify'] = 0 - data = _urlencode(data) - elif api_version == 'v2': - headers['Content-Type'] = 'application/json' - headers['Authorization'] = 'Bearer {0}'.format(api_key) - if data: - data = salt.utils.json.dumps(data) - else: - log.error('Unsupported HipChat API version') - return False - - result = salt.utils.http.query( - url, - method, - params=query_params, - data=data, - decode=True, - status=True, - header_dict=headers, - opts=__opts__, - ) - - if result.get('status', None) == salt.ext.six.moves.http_client.OK: - response = hipchat_functions.get(api_version).get(function).get('response') - return result.get('dict', {}).get(response, None) - elif result.get('status', None) == salt.ext.six.moves.http_client.NO_CONTENT: - return False - else: - log.debug(url) - log.debug(query_params) - log.debug(data) - log.debug(result) - if result.get('error'): - log.error(result) - return False - - -def _send_message(room_id, - message, - from_name, - api_key=None, - api_version=None, - api_url=None, - color=None, - notify=False): - ''' - Send a message to a HipChat room. - :param room_id: The room id or room name, either will work. - :param message: The message to send to the HipChat room. - :param from_name: Specify who the message is from. - :param api_url: The HipChat API URL, if not specified in the configuration. - :param api_key: The HipChat api key, if not specified in the configuration. - :param api_version: The HipChat api version, if not specified in the configuration. - :param color: The color for the message, default: yellow. - :param notify: Whether to notify the room, default: False. - :return: Boolean if message was sent successfully. - ''' - - parameters = dict() - parameters['room_id'] = room_id - parameters['from'] = from_name[:15] - parameters['message'] = message[:10000] - parameters['message_format'] = 'text' - parameters['color'] = color - parameters['notify'] = notify - - result = _query(function='message', - api_key=api_key, - api_version=api_version, - room_id=room_id, - api_url=api_url, - method='POST', - data=parameters) - - if result: - return True - else: - return False - - -def _verify_options(options): - ''' - Verify Hipchat options and log warnings - - Returns True if all options can be verified, - otherwise False - ''' - if not options.get('room_id'): - log.error('hipchat.room_id not defined in salt config') - return False - - if not options.get('from_name'): - log.error('hipchat.from_name not defined in salt config') - return False - - if not options.get('api_key'): - log.error('hipchat.api_key not defined in salt config') - return False - - if not options.get('api_version'): - log.error('hipchat.api_version not defined in salt config') - return False - - return True - - -def returner(ret): - ''' - Send an hipchat message with the return data from a job - ''' - - _options = _get_options(ret) - - if not _verify_options(_options): - return - - message = ('id: {0}\r\n' - 'function: {1}\r\n' - 'function args: {2}\r\n' - 'jid: {3}\r\n' - 'return: {4}\r\n').format( - ret.get('id'), - ret.get('fun'), - ret.get('fun_args'), - ret.get('jid'), - pprint.pformat(ret.get('return'))) - - if ret.get('retcode') == 0: - color = _options.get('color') - else: - color = 'red' - - hipchat = _send_message(_options.get('room_id'), # room_id - message, # message - _options.get('from_name'), # from_name - api_key=_options.get('api_key'), - api_version=_options.get('api_version'), - api_url=_options.get('api_url'), - color=color, - notify=_options.get('notify')) - - return hipchat - - -def event_return(events): - ''' - Return event data to hipchat - ''' - _options = _get_options() - - for event in events: - # TODO: - # Pre-process messages to apply individualized colors for various - # event types. - log.trace('Hipchat returner received event: %s', event) - _send_message(_options.get('room_id'), # room_id - event['data'], # message - _options.get('from_name'), # from_name - api_key=_options.get('api_key'), - api_version=_options.get('api_version'), - api_url=_options.get('api_url'), - color=_options.get('color'), - notify=_options.get('notify')) diff --git a/salt/states/hipchat.py b/salt/states/hipchat.py deleted file mode 100644 index cb31d58dd9c7..000000000000 --- a/salt/states/hipchat.py +++ /dev/null @@ -1,144 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Send a message to Hipchat -========================= - -This state is useful for sending messages to Hipchat during state runs. - -The property api_url is optional. By defaul will use the public HipChat API at https://api.hipchat.com - -.. versionadded:: 2015.5.0 - -.. code-block:: yaml - - hipchat-message: - hipchat.send_message: - - room_id: 123456 - - from_name: SuperAdmin - - message: 'This state was executed successfully.' - - api_url: https://hipchat.myteam.com - - api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - - api_version: v1 - -The api key can be specified in the master or minion configuration like below: - -.. code-block:: yaml - - hipchat: - api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - api_version: v1 - -''' - -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals - - -def __virtual__(): - ''' - Only load if the hipchat module is available in __salt__ - ''' - return 'hipchat' if 'hipchat.send_message' in __salt__ else False - - -def send_message(name, - room_id, - from_name, - message, - api_url=None, - api_key=None, - api_version=None, - message_color='yellow', - notify=False): - ''' - Send a message to a Hipchat room. - - .. code-block:: yaml - - hipchat-message: - hipchat.send_message: - - room_id: 123456 - - from_name: SuperAdmin - - message: 'This state was executed successfully.' - - api_url: https://hipchat.myteam.com - - api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - - api_version: v1 - - message_color: green - - notify: True - - The following parameters are required: - - name - The unique name for this event. - - room_id - The room to send the message to. Can either be the ID or the name. - - from_name - The name of that is to be shown in the "from" field. - If not specified, defaults to. - - message - The message that is to be sent to the Hipchat room. - - The following parameters are optional: - - api_url - The API URl to be used. - If not specified here or in the configuration options of master or minion, - will use the public HipChat API: https://api.hipchat.com - - api_key - The api key for Hipchat to use for authentication, - if not specified in the configuration options of master or minion. - - api_version - The api version for Hipchat to use, - if not specified in the configuration options of master or minion. - - message_color - The color the Hipchat message should be displayed in. One of the following, default: yellow - "yellow", "red", "green", "purple", "gray", or "random". - - notify - Should a notification in the room be raised. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - - if __opts__['test']: - ret['comment'] = 'The following message is to be sent to Hipchat: {0}'.format(message) - ret['result'] = None - return ret - - if not room_id: - ret['comment'] = 'Hipchat room id is missing: {0}'.format(name) - return ret - - if not from_name: - ret['comment'] = 'Hipchat from name is missing: {0}'.format(name) - return ret - - if not message: - ret['comment'] = 'Hipchat message is missing: {0}'.format(name) - return ret - - ret['result'] = __salt__['hipchat.send_message']( - room_id=room_id, - message=message, - from_name=from_name, - api_url=api_url, - api_key=api_key, - api_version=api_version, - color=message_color, - notify=notify, - ) - - if ret and ret['result']: - ret['comment'] = 'Sent message: {0}'.format(name) - else: - ret['comment'] = 'Failed to send message: {0}'.format(name) - - return ret diff --git a/tests/unit/modules/test_hipchat.py b/tests/unit/modules/test_hipchat.py deleted file mode 100644 index b31be8c93398..000000000000 --- a/tests/unit/modules/test_hipchat.py +++ /dev/null @@ -1,93 +0,0 @@ -# -*- coding: utf-8 -*- -''' - :codeauthor: Jayesh Kariya -''' - -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, - NO_MOCK, - NO_MOCK_REASON -) - -# Import Salt Libs -import salt.modules.hipchat as hipchat - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -class HipchatTestCase(TestCase, LoaderModuleMockMixin): - ''' - Test cases for salt.modules.hipchat - ''' - def setup_loader_modules(self): - return {hipchat: {}} - - # 'list_rooms' function tests: 1 - - def test_list_rooms(self): - ''' - Test if it list all HipChat rooms. - ''' - with patch('salt.modules.hipchat._query', MagicMock(return_value=True)): - self.assertEqual(hipchat.list_rooms(), True) - - # 'list_users' function tests: 1 - - def test_list_users(self): - ''' - Test if it list all HipChat users. - ''' - with patch('salt.modules.hipchat._query', MagicMock(return_value=True)): - self.assertEqual(hipchat.list_users(), True) - - # 'find_room' function tests: 1 - - def test_find_room(self): - ''' - Test if it find a room by name and return it. - ''' - mock = MagicMock(return_value=[{'name': 'Development Room'}]) - with patch.object(hipchat, 'list_rooms', mock): - self.assertEqual(hipchat.find_room('Development Room'), - {'name': 'Development Room'}) - - self.assertEqual(hipchat.find_room('QA Room'), False) - - # 'find_user' function tests: 1 - - def test_find_user(self): - ''' - Test if it find a user by name and return it. - ''' - mock = MagicMock(return_value=[{'name': 'Thomas Hatch'}]) - with patch.object(hipchat, 'list_rooms', mock): - self.assertEqual(hipchat.find_room('Thomas Hatch'), - {'name': 'Thomas Hatch'}) - - self.assertEqual(hipchat.find_user('Salt QA'), False) - - # 'send_message' function tests: 1 - - def test_send_message(self): - ''' - Test if it send a message to a HipChat room. - ''' - with patch('salt.modules.hipchat._query', MagicMock(return_value=True)): - self.assertEqual(hipchat.send_message('Development Room', - 'Build is done', - 'Build Server'), True) - - def test_send_message_false(self): - ''' - Test if it send a message to a HipChat room. - ''' - with patch('salt.modules.hipchat._query', MagicMock(return_value=False)): - self.assertEqual(hipchat.send_message('Development Room', - 'Build is done', - 'Build Server'), False) diff --git a/tests/unit/states/test_hipchat.py b/tests/unit/states/test_hipchat.py deleted file mode 100644 index 722433a4af99..000000000000 --- a/tests/unit/states/test_hipchat.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- -''' - :codeauthor: Jayesh Kariya -''' -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, - MagicMock, - patch) - -# Import Salt Libs -import salt.states.hipchat as hipchat - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -class HipchatTestCase(TestCase, LoaderModuleMockMixin): - ''' - Test cases for salt.states.hipchat - ''' - def setup_loader_modules(self): - return {hipchat: {}} - - # 'send_message' function tests: 1 - - def test_send_message(self): - ''' - Test to send a message to a Hipchat room. - ''' - name = 'salt' - room_id = '123456' - from_name = 'SuperAdmin' - message = 'This state was executed successfully.' - - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} - - with patch.dict(hipchat.__opts__, {'test': True}): - comt = ('The following message is to be sent to Hipchat: {0}' - .format(message)) - ret.update({'comment': comt}) - self.assertDictEqual(hipchat.send_message(name, room_id, from_name, - message), ret) - - with patch.dict(hipchat.__opts__, {'test': False}): - comt = ('Hipchat room id is missing: {0}'.format(name)) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(hipchat.send_message(name, None, from_name, - message), ret) - - comt = ('Hipchat from name is missing: {0}'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(hipchat.send_message(name, room_id, None, - message), ret) - - comt = ('Hipchat message is missing: {0}'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(hipchat.send_message(name, room_id, from_name, - None), ret) - - mock = MagicMock(return_value=True) - with patch.dict(hipchat.__salt__, {'hipchat.send_message': mock}): - comt = ('Sent message: {0}'.format(name)) - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(hipchat.send_message(name, room_id, - from_name, message), - ret)