# -*- coding: utf-8 -*- ''' HTTP monitoring states Perform an HTTP query and statefully return the result TODO: This is a copy of upstream state file: https://github.com/saltstack/salt/blob/2017.7.3/salt/states/http.py. It have to be removed when MCP Salt will be upgreaded up to 2017 or higher. .. versionadded:: 2015.5.0 ''' # Import python libs from __future__ import absolute_import import re import logging import time __monitor__ = [ 'query', ] log = logging.getLogger(__name__) def query(name, match=None, match_type='string', status=None, wait_for=None, **kwargs): ''' Perform an HTTP query and statefully return the result .. versionadded:: 2015.5.0 name The name of the query. match Specifies a pattern to look for in the return text. By default, this will perform a string comparison of looking for the value of match in the return text. match_type Specifies the type of pattern matching to use. Default is ``string``, but can also be set to ``pcre`` to use regular expression matching if a more complex pattern matching is required. .. note:: Despite the name of ``match_type`` for this argument, this setting actually uses Python's ``re.search()`` function rather than Python's ``re.match()`` function. status The status code for a URL for which to be checked. Can be used instead of or in addition to the ``match`` setting. If both ``match`` and ``status`` options are set, both settings will be checked. However, note that if only one option is ``True`` and the other is ``False``, then ``False`` will be returned. If this case is reached, the comments in the return data will contain troubleshooting information. For more information about the ``http.query`` state, refer to the :ref:`HTTP Tutorial `. .. code-block:: yaml query_example: http.query: - name: 'http://example.com/' - status: 200 ''' # Monitoring state, but changes may be made over HTTP ret = {'name': name, 'result': None, 'comment': '', 'changes': {}, 'data': {}} # Data field for monitoring state if match is None and status is None: ret['result'] = False ret['comment'] += ( ' Either match text (match) or a status code (status) is required.' ) return ret if 'decode' not in kwargs: kwargs['decode'] = False kwargs['text'] = True kwargs['status'] = True if __opts__['test']: kwargs['test'] = True if wait_for: data = __salt__['http.wait_for_successful_query'](name, wait_for=wait_for, **kwargs) else: data = __salt__['http.query'](name, **kwargs) if match is not None: if match_type == 'string': if match in data.get('text', ''): ret['result'] = True ret['comment'] += ' Match text "{0}" was found.'.format(match) else: ret['result'] = False ret['comment'] += ' Match text "{0}" was not found.'.format(match) elif match_type == 'pcre': if re.search(match, data.get('text', '')): ret['result'] = True ret['comment'] += ' Match pattern "{0}" was found.'.format(match) else: ret['result'] = False ret['comment'] += ' Match pattern "{0}" was not found.'.format(match) if status is not None: if data.get('status', '') == status: ret['comment'] += 'Status {0} was found, as specified.'.format(status) if ret['result'] is None: ret['result'] = True else: ret['comment'] += 'Status {0} was not found, as specified.'.format(status) ret['result'] = False if __opts__['test'] is True: ret['result'] = None ret['comment'] += ' (TEST MODE' if 'test_url' in kwargs: ret['comment'] += ', TEST URL WAS: {0}'.format(kwargs['test_url']) ret['comment'] += ')' ret['data'] = data return ret def wait_for_successful_query(name, wait_for=300, **kwargs): ''' Like query but, repeat and wait until match/match_type or status is fulfilled. State returns result from last query state in case of success or if no successful query was made within wait_for timeout. name The name of the query. wait_for Total time to wait for requests that succeed. request_interval Optional interval to delay requests by N seconds to reduce the number of requests sent. .. note:: All other arguements are passed to the http.query state. ''' starttime = time.time() while True: caught_exception = None ret = None try: ret = query(name, **kwargs) if ret['result']: return ret except Exception as exc: caught_exception = exc if time.time() > starttime + wait_for: if not ret and caught_exception: # workaround pylint bug https://www.logilab.org/ticket/3207 raise caught_exception # pylint: disable=E0702 return ret else: # Space requests out by delaying for an interval if 'request_interval' in kwargs: log.debug("delaying query for {0} seconds.".format(kwargs['request_interval'])) time.sleep(kwargs['request_interval'])