blob: a539ec0d0b9e3b93fd8f95853bde95fc5388d8f4 [file] [log] [blame]
Oleg Iurchenkodf770942018-01-23 13:37:08 +02001# -*- coding: utf-8 -*-
2'''
3HTTP monitoring states
4
5Perform an HTTP query and statefully return the result
6
7TODO: This is a copy of upstream state file: https://github.com/saltstack/salt/blob/2017.7.3/salt/states/http.py.
8It have to be removed when MCP Salt will be upgreaded up to 2017 or higher.
9
10.. versionadded:: 2015.5.0
11'''
12
13# Import python libs
14from __future__ import absolute_import
15import re
16import logging
17import time
18
19__monitor__ = [
20 'query',
21 ]
22
23log = logging.getLogger(__name__)
24
25
26def query(name, match=None, match_type='string', status=None, wait_for=None, **kwargs):
27 '''
28 Perform an HTTP query and statefully return the result
29
30 .. versionadded:: 2015.5.0
31
32 name
33 The name of the query.
34
35 match
36 Specifies a pattern to look for in the return text. By default, this will
37 perform a string comparison of looking for the value of match in the return
38 text.
39
40 match_type
41 Specifies the type of pattern matching to use. Default is ``string``, but
42 can also be set to ``pcre`` to use regular expression matching if a more
43 complex pattern matching is required.
44
45 .. note::
46
47 Despite the name of ``match_type`` for this argument, this setting
48 actually uses Python's ``re.search()`` function rather than Python's
49 ``re.match()`` function.
50
51 status
52 The status code for a URL for which to be checked. Can be used instead of
53 or in addition to the ``match`` setting.
54
55 If both ``match`` and ``status`` options are set, both settings will be checked.
56 However, note that if only one option is ``True`` and the other is ``False``,
57 then ``False`` will be returned. If this case is reached, the comments in the
58 return data will contain troubleshooting information.
59
60 For more information about the ``http.query`` state, refer to the
61 :ref:`HTTP Tutorial <tutorial-http>`.
62
63 .. code-block:: yaml
64
65 query_example:
66 http.query:
67 - name: 'http://example.com/'
68 - status: 200
69
70 '''
71 # Monitoring state, but changes may be made over HTTP
72 ret = {'name': name,
73 'result': None,
74 'comment': '',
75 'changes': {},
76 'data': {}} # Data field for monitoring state
77
78 if match is None and status is None:
79 ret['result'] = False
80 ret['comment'] += (
81 ' Either match text (match) or a status code (status) is required.'
82 )
83 return ret
84
85 if 'decode' not in kwargs:
86 kwargs['decode'] = False
87 kwargs['text'] = True
88 kwargs['status'] = True
89 if __opts__['test']:
90 kwargs['test'] = True
91
92 if wait_for:
93 data = __salt__['http.wait_for_successful_query'](name, wait_for=wait_for, **kwargs)
94 else:
95 data = __salt__['http.query'](name, **kwargs)
96
97 if match is not None:
98 if match_type == 'string':
99 if match in data.get('text', ''):
100 ret['result'] = True
101 ret['comment'] += ' Match text "{0}" was found.'.format(match)
102 else:
103 ret['result'] = False
104 ret['comment'] += ' Match text "{0}" was not found.'.format(match)
105 elif match_type == 'pcre':
106 if re.search(match, data.get('text', '')):
107 ret['result'] = True
108 ret['comment'] += ' Match pattern "{0}" was found.'.format(match)
109 else:
110 ret['result'] = False
111 ret['comment'] += ' Match pattern "{0}" was not found.'.format(match)
112
113 if status is not None:
114 if data.get('status', '') == status:
115 ret['comment'] += 'Status {0} was found, as specified.'.format(status)
116 if ret['result'] is None:
117 ret['result'] = True
118 else:
119 ret['comment'] += 'Status {0} was not found, as specified.'.format(status)
120 ret['result'] = False
121
122 if __opts__['test'] is True:
123 ret['result'] = None
124 ret['comment'] += ' (TEST MODE'
125 if 'test_url' in kwargs:
126 ret['comment'] += ', TEST URL WAS: {0}'.format(kwargs['test_url'])
127 ret['comment'] += ')'
128
129 ret['data'] = data
130 return ret
131
132
133def wait_for_successful_query(name, wait_for=300, **kwargs):
134 '''
135 Like query but, repeat and wait until match/match_type or status is fulfilled. State returns result from last
136 query state in case of success or if no successful query was made within wait_for timeout.
137
138 name
139 The name of the query.
140
141 wait_for
142 Total time to wait for requests that succeed.
143
144 request_interval
145 Optional interval to delay requests by N seconds to reduce the number of requests sent.
146
147 .. note::
148
149 All other arguements are passed to the http.query state.
150 '''
151 starttime = time.time()
152
153 while True:
154 caught_exception = None
155 ret = None
156 try:
157 ret = query(name, **kwargs)
158 if ret['result']:
159 return ret
160 except Exception as exc:
161 caught_exception = exc
162
163 if time.time() > starttime + wait_for:
164 if not ret and caught_exception:
165 # workaround pylint bug https://www.logilab.org/ticket/3207
166 raise caught_exception # pylint: disable=E0702
167 return ret
168 else:
169 # Space requests out by delaying for an interval
170 if 'request_interval' in kwargs:
171 log.debug("delaying query for {0} seconds.".format(kwargs['request_interval']))
172 time.sleep(kwargs['request_interval'])