Completed
Push — master ( 529ef4...25b963 )
by Gareth
41s
created

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