Completed
Push — master ( 69eb77...24673c )
by Gareth
01:37
created

Cachet.checkInitialPing()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
#!/usr/bin/python
2
# coding=utf-8
3
4
import requests
5
import httplib
6
import time
7
from system.logging import Logger
8
9
from utils import Utils
10
11
12
# Copyright 2017 Gareth Williams
13
#
14
# Licensed under the Apache License, Version 2.0 (the "License");
15
# you may not use this file except in compliance with the License.
16
# You may obtain a copy of the License at
17
#
18
#     http://www.apache.org/licenses/LICENSE-2.0
19
#
20
# Unless required by applicable law or agreed to in writing, software
21
# distributed under the License is distributed on an "AS IS" BASIS,
22
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
# See the License for the specific language governing permissions and
24
# limitations under the License.
25
#
26
#
27
# Incident Statuses
28
#
29
# 0 = Scheduled
30
# 1 = Investigating
31
# 2 = Identified
32
# 3 = Watching
33
# 4 = Fixed
34
#
35
# Component Statuses
36
#
37
# 1 = Operational
38
# 2 = Performance Issues
39
# 3 = Partial Outage
40
# 4 = Major Outage
41
42
43
44
class Cachet(object):
45
    httpErrors = {
46
        # Cloudflare Errors
47
        520: "Web server is returning an unknown error (Cloudflare)",
48
        521: "Web server is down (Cloudflare)",
49
        522: "Connection timed out (Cloudflare)",
50
        523: "Origin is unreachable (Cloudflare)",
51
        524: "A timeout occurred (Cloudflare)",
52
        525: "SSL handshake failed (Cloudflare)",
53
        526: "Invalid SSL certificate (Cloudflare)",
54
        # Nginx Errors
55
        444: "No Response (Nginx)",
56
        494: "Request Header Too Large (Nginx)",
57
        495: "Cert Error (Nginx)",
58
        496: "No Cert (Nginx)",
59
        497: "HTTP to HTTPS (Nginx)",
60
        499: "Client Closed Request (Nginx)",
61
        # Other
62
        # 1xx
63
        102: "Server has received and is processing the request",
64
        103: "resume aborted PUT or POST requests",
65
        122: "URI is longer than a maximum of 2083 characters",
66
        # 2xx
67
        207: "XML return with possible multiple seperate responses.",
68
        208: "The results are previously returned.",
69
        226: "The request has been fulfilled amd response is in instance manipulations.",
70
        # 3xx
71
        308: "Please connect again to the different URL using the same method.",
72
        # 4xx
73
        418: "I'm a teapot.",
74
        420: "Method Failure",
75
        421: "Enhance Your Calm",
76
        422: "Unprocessable Entity",
77
        423: "Locked",
78
        424: "Failed Dependency",
79
        426: "Upgrade Required",
80
        428: "Precondition Required",
81
        429: "Too Many Requests",
82
        431: "Request Header Fields Too Large",
83
        440: "Login Timeout (Microsoft)",
84
        449: "Retry With (Microsoft)",
85
        450: "Blocked by Windows Parental Controls",
86
        451: "Unavailable For Legal Reasons",
87
        # 5xx
88
        506: "Variant Also Negotiates (RFC 2295)",
89
        507: "Insufficient Storage (WebDAV; RFC 4918)",
90
        508: "Loop Detected (WebDAV; RFC 5842)",
91
        509: "Bandwidth Limit Exceeded (Apache bw/limited extension)",
92
        510: "Not Extended (RFC 2774)",
93
        511: "Network Authentication Required"
94
95
    }
96
97
    def __init__(self):
98
        self.logs = Logger()
99
        self.utils = Utils()
100
        self.config = self.utils.readConfig()
101
        self.base_url = self.config['api_url']
102
        self.api_token = self.config['api_token']
103
        self.maxRetries = self.config['retries']
104
105
        try:
106
            if self.utils.ping().status_code == 200 and self.utils.ping().json()['data'] == "Pong!":
107
                self.checkSites()
108
            else:
109
                exit(1)
110
        except Exception as e:
111
            self.logs.error(e)
112
            exit(1)
113
114
    def checkSites(self):
115
        # Count how many sites to monitor
116
        monitor_count = len(self.config['monitoring'])
117
        x = 0
118
119
        # Loop through sites to monitor
120
        while x < monitor_count:
121
122
            isEnabled = self.config['monitoring'][x]['enabled']
123
            status_codes = self.config['monitoring'][x]['expected_status_code']
124
            url = self.config['monitoring'][x]['url']
125
            request_method = self.config['monitoring'][x]['method']
126
            c_id = self.config['monitoring'][x]['component_id']
127
            localtime = time.asctime(time.localtime(time.time()))
128
            current_status = self.utils.getComponentsByID(c_id).json()['data']['status']
129
            incident_id = self.checkForIncident(c_id)
130
            check_timeout = self.config['monitoring'][x]['timeout']
131
132
            try:
133
                if self.checkInitialPing() == 200:
134
                    if isEnabled:
135
                        if request_method.lower() == "get":
136
                            r = requests.get(url, verify=True, timeout=check_timeout)
137
                            # self.utils.postMetricsPointsByID(1, r.elapsed.total_seconds() * 1000)
138
                            if r.status_code not in status_codes and r.status_code not in self.httpErrors:
139
                                error_code = '%s check **failed** - %s \n\n`%s %s HTTP StatusError: %s`' % (
140
                                url, localtime, request_method, url, httplib.responses[r.status_code])
141
                                c_status = 4
142
                                if not incident_id:
143
                                    self.utils.postIncidents('%s: HTTP Status Error' % url, error_code, 1, 1,
144
                                                             component_id=c_id, component_status=c_status)
145
                                if current_status is not 4:
146
                                    self.utils.putComponentsByID(c_id, status=c_status)
147
                                self.logs.warn("%s" % error_code.replace('\n', '').replace('`', ''))
148 View Code Duplication
                            elif r.status_code not in status_codes and r.status_code in self.httpErrors:
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
149
                                error_code = '%s check **failed** - %s \n\n`%s %s HTTP Status Error: %s`' % (
150
                                url, localtime, request_method, url, self.httpErrors[r.status_code])
151
                                c_status = 4
152
                                if not incident_id:
153
                                    self.utils.postIncidents('%s: HTTP Status Error' % url, error_code, 1, 1,
154
                                                             component_id=c_id, component_status=c_status)
155
                                if current_status is not 4:
156
                                    self.utils.putComponentsByID(c_id, status=c_status)
157
                                self.logs.warn("%s" % error_code.replace('\n', '').replace('`', ''))
158
                        elif request_method.lower() == "post":
159
                            r = requests.get(url, verify=True, timeout=check_timeout)
160
                            if r.status_code not in status_codes and r.status_code not in self.httpErrors:
161
                                error_code = '%s check **failed** - %s \n\n`%s %s HTTP Status Error: %s`' % (
162
                                url, localtime, request_method, url, httplib.responses[r.status_code])
163
                                c_status = 4
164
                                if not incident_id:
165
                                    self.utils.postIncidents('%s: HTTP Status Error' % url, error_code, 1, 1,
166
                                                             component_id=c_id, component_status=c_status)
167
                                if current_status is not 4:
168
                                    self.utils.putComponentsByID(c_id, status=c_status)
169
                                self.logs.warn("%s" % error_code.replace('\n', '').replace('`', ''))
170 View Code Duplication
                            elif r.status_code not in status_codes and r.status_code in self.httpErrors:
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
171
                                error_code = '%s check **failed** - %s \n\n`%s %s HTTP Status Error: %s`' % (
172
                                url, localtime, request_method, url, self.httpErrors[r.status_code])
173
                                c_status = 4
174
                                if not incident_id:
175
                                    self.utils.postIncidents('%s: HTTP Status Error' % url, error_code, 1, 1,
176
                                                             component_id=c_id, component_status=c_status)
177
                                if current_status is not 4:
178
                                    self.utils.putComponentsByID(c_id, status=c_status)
179
                                self.logs.warn("%s" % error_code.replace('\n', '').replace('`', ''))
180
                else:
181
                    self.logs.error("Unable to connect to %s" % self.base_url)
182
            except requests.exceptions.HTTPError as e:
183
                error_code = '%s check **failed** - %s \n\n`%s %s HTTP Error: %s`' % (
184
                url, localtime, request_method, url, e)
185
                c_status = 4
186
                if not incident_id:
187
                    self.utils.postIncidents('%s: HTTP Error' % url, error_code, 1, 1, component_id=c_id,
188
                                             component_status=c_status)
189
                if current_status is not 4:
190
                    self.utils.putComponentsByID(c_id, status=c_status)
191
                self.logs.warn(error_code.replace('\n', '').replace('`', ''))
192
            except requests.exceptions.SSLError as e:
193
                error_code = '%s check **failed** - %s \n\n`%s %s SSL Error: %s`' % (
194
                url, localtime, request_method, url, e)
195
                c_status = 4
196
                if not incident_id:
197
                    self.utils.postIncidents('%s: SSL Error' % url, error_code, 1, 1, component_id=c_id,
198
                                             component_status=c_status)
199
                if current_status is not 4:
200
                    self.utils.putComponentsByID(c_id, status=c_status)
201
                self.logs.warn(error_code.replace('\n', '').replace('`', ''))
202
            except requests.exceptions.ConnectionError as e:
203
                error_code = '%s check **failed** - %s \n\n`%s %s Connection Error: %s`' % (
204
                url, localtime, request_method, url, e)
205
                c_status = 4
206
                if not incident_id:
207
                    self.utils.postIncidents('%s: Connection Error' % url, error_code, 1, 1, component_id=c_id,
208
                                             component_status=c_status)
209
                if current_status is not 4:
210
                    self.utils.putComponentsByID(c_id, status=c_status)
211
                self.logs.warn(error_code.replace('\n', '').replace('`', ''))
212
            except requests.exceptions.Timeout as e:
213
                error_code = '%s check **failed** - %s \n\n`%s %s Request Timeout: %s`' % (
214
                url, localtime, request_method, url, e)
215
                c_status = 2
216
                if not incident_id:
217
                    self.utils.postIncidents('%s: Request Timeout' % url, error_code, 1, 1, component_id=c_id,
218
                                             component_status=c_status)
219
                if current_status is not 4:
220
                    self.utils.putComponentsByID(c_id, status=c_status)
221
                self.logs.warn(error_code.replace('\n', '').replace('`', ''))
222
            except requests.exceptions.TooManyRedirects as e:
223
                error_code = '%s check **failed** - %s \n\n`%s %s Too Many Redirects: %s`' % (
224
                url, localtime, request_method, url, e)
225
                c_status = 4
226
                if not incident_id:
227
                    self.utils.postIncidents('%s: Too Many Redirects' % url, error_code, 1, 1, component_id=c_id,
228
                                             component_status=c_status)
229
                if current_status is not 4:
230
                    self.utils.putComponentsByID(c_id, status=c_status)
231
                self.logs.warn(error_code.replace('\n', '').replace('`', ''))
232
            except requests.exceptions.RetryError as e:
233
                error_code = '%s check **failed** - %s \n\n`%s %s Retry Error: %s`' % (
234
                url, localtime, request_method, url, e)
235
                c_status = 4
236
                if not incident_id:
237
                    self.utils.postIncidents('%s: Retry Error' % url, error_code, 1, 1, component_id=c_id,
238
                                             component_status=c_status)
239
                if current_status is not 4:
240
                    self.utils.putComponentsByID(c_id, status=c_status)
241
                self.logs.warn(error_code.replace('\n', '').replace('`', ''))
242
            except httplib.BadStatusLine as e:
243
                self.logs.error("%s \nCould not fetch %s" % (e, url))
244
            except Exception as e:
245
                error_code = '%s check **failed** - %s \n\n`%s %s Unexpected Error: %s`' % (
246
                url, localtime, request_method, url, e)
247
                c_status = 4
248
                if not incident_id:
249
                    self.utils.postIncidents('%s: Unexpected Error' % url, error_code, 1, 1, component_id=c_id,
250
                                             component_status=c_status)
251
                if current_status is not 4:
252
                    self.utils.putComponentsByID(c_id, status=c_status)
253
                self.logs.error(error_code.replace('\n', '').replace('`', ''))
254
            else:
255
                if r.status_code in status_codes:
256
                    if current_status is not 1 and not incident_id:
257
                        self.utils.putComponentsByID(c_id, status=1)
258
                        self.logs.info("Issue with %s has been resolved" % url)
259
                    elif current_status is not 1 and incident_id:
260
                        incident_description = "Resolved at %s\n\n***\n\n%s" % (
261
                        localtime, self.getIncidentInfo(incident_id))
262
                        self.utils.putIncidentsByID(incident_id, message=incident_description, status=4,
263
                                                    component_id=c_id, component_status=1)
264
                    else:
265
                        self.logs.info("%s no issues found" % url)
266
267
            x += 1
268
        self.logs.info("############################")
269
270
    def checkForIncident(self, component_id):
271
        current_incidents = self.utils.getIncidents().json()
272
        incidents = len(current_incidents['data'])
273
        x = 0
274
275
        while x < incidents:
276
            incident_id = current_incidents['data'][x]['id']
277
            incident_component_id = current_incidents['data'][x]['component_id']
278
            incident_status = current_incidents['data'][x]['status']
279
280
            if component_id == incident_component_id and incident_status is not 4:
281
                return incident_id
282
            x += 1
283
284
    def getIncidentInfo(self, i_id):
285
        incident = self.utils.getIncidentsByID(i_id).json()
286
        i_description = incident['data']['message']
287
        return i_description
288
289
    def checkInitialPing(self):
290
        r = requests.get(self.base_url, verify=True, timeout=10)
291
        return r.status_code
292