Merge "Add httpng.py state"
diff --git a/_states/httpng.py b/_states/httpng.py
new file mode 100644
index 0000000..a539ec0
--- /dev/null
+++ b/_states/httpng.py
@@ -0,0 +1,172 @@
+# -*- 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'])