| # -*- 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 <tutorial-http>`. |
| |
| .. 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']) |