def get_constantcontact_status(url="https://status.constantcontact.com/"): # constant contact returns a 403 forbidden when missing headers headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:39.0) Gecko/20100101 Firefox/39.0'} r = http_requests_get_to_dict(url, headers=headers) _resp = { "response": dict(), "headers": dict(), "status": "ok" } _filtered_headers = { "date": r.headers.get("date", ""), "status_code": r.headers.get("status_code", 200) } _resp["headers"] = _filtered_headers # sendgrid uses nested service statuses _resp["response"]["services"] = dict() # not really into this as a list, but will adjust as needed _resp["response"]["outages"] = [] if not r.error and r.http_2xx: _rtxml = html_string(r.text) _body = _rtxml.xpath("//html/body")[0] _content_row = _body.xpath("section[@id='status-content']/article/section[@class='entry-content']/section[@class='row']/main/table[contains(@class, 'table')]") _tbody_rows = _content_row[0].xpath("tbody/tr") _serv_status = {} for _service_row in _tbody_rows: _service_row_td_collection = _service_row.xpath("td") _service_name = _service_row_td_collection[0].text.strip() _service_status = _service_row_td_collection[2].text.strip() if _service_status == "All Systems are Available and Operating Normally": _service_status = "ok" else: _resp["response"]["outages"].append(_service_name) _serv_status[_service_name] = _service_status _resp["response"]["services"] = _serv_status else: _resp["status"] = "nok" return _resp
def get_dropbox_website_status(url="https://status.dropbox.com/"): _resp = { "response": dict(), "headers": dict() } try: r = requests.get(url) rc = r.status_code rt = r.text rheaders = r.headers # headers we should expect from dropbox _resp["headers"]["status_code"] = rc _resp["headers"]["date"] = rheaders.get("date", "") _resp["headers"]["etag"] = rheaders.get("etag", "") if str(rc).startswith("2"): if "json" in rheaders["content-type"]: _resp["response"] = "unexpected content-type. expected text/html. (good news -- we can update our plugin!)" else: _rtxml = html_string(rt) _rtxml_status_line = _rtxml.xpath('//html/body/div[@id="status-line"]/h1')[0] _rtxml_status_block = _rtxml_status_line.text if _rtxml_status_block == "Dropbox is running normally.": _resp["status"] = "ok" _resp["response"] = str(_rtxml_status_block) return _resp except ConnectionError as e: _resp["response"] = str(e) # if ConnectionError exception or not an http 2xx _resp["status"] = "nok" return _resp
def get_dropbox_website_status(url="https://status.dropbox.com/"): _resp = {"response": dict(), "headers": dict()} try: r = requests.get(url) rc = r.status_code rt = r.text rheaders = r.headers # headers we should expect from dropbox _resp["headers"]["status_code"] = rc _resp["headers"]["date"] = rheaders.get("date", "") _resp["headers"]["etag"] = rheaders.get("etag", "") if str(rc).startswith("2"): if "json" in rheaders["content-type"]: _resp[ "response"] = "unexpected content-type. expected text/html. (good news -- we can update our plugin!)" else: _rtxml = html_string(rt) _rtxml_status_line = _rtxml.xpath( '//html/body/div[@id="status-line"]/h1')[0] _rtxml_status_block = _rtxml_status_line.text if _rtxml_status_block == "Dropbox is running normally.": _resp["status"] = "ok" _resp["response"] = str(_rtxml_status_block) return _resp except ConnectionError as e: _resp["response"] = str(e) # if ConnectionError exception or not an http 2xx _resp["status"] = "nok" return _resp
def get_html(self, first = 1, last=-1): first = max(0,first) if last < 0 : last = len(self.bookinfo.chapters) else : last = max(last,first) metas = [] metas.append(E.META(name="author", value=self.bookinfo.author )) metas.append(E.META(name="DC.title", value=self.bookinfo.title )) if hasattr(self.bookinfo, "year") : metas.append(E.META(name="date", value=self.bookinfo.year+"-00-00")) htmlbody = E.BODY(E.H1(escape(self.bookinfo.title))) target = E.HTML( E.HEAD( lxml.html.fromstring( "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">" ), E.TITLE(escape(self.bookinfo.title)), *metas ), htmlbody ) for chap in range(first - 1, last) : chapter = self.bookinfo.chapters[chap] content = self.fetch( self.gutenberg_url.format( book=self.book, chapter = chapter, ) ).get_element_by_id("gutenb") content.set("id","chapter"+chapter) for tags in self.chapter_tags : for tag in content.iterchildren(tag=tags) : tag.set("class","chapter") htmlbody.append(content) return html_string(target)
def get_aws_endpoint_status(self, regions=None): # todo: needs a "i only care about service x,y,z" if not regions: regions = self.regions r = requests.get(self.status_endpoint) # need to log the following types of errors and handle them # try: # r = requests.get(endpoint) # except requests.ConnectionError: # except requests.Timeout: # except requests.HTTPError: # except requests.TooManyRedirects: rc = r.status_code rt = r.text rheaders = r.headers _resp = { "response": {}, "headers": {} } _resp["headers"]["status_code"] = rc # not the same as date of response _resp["headers"]["last-modified"] = rheaders.get("last-modified", "") _resp["headers"]["etag"] = rheaders.get("etag", "") # todo: convert date to standard format or return "now" _resp["headers"]["date"] = rheaders.get("date", "") if str(rc).startswith("2"): # if we received an OK from Amazon, then continue processing if "json" in rheaders["content-type"]: # this would be unexpected -- amazon returns text/html for their status page. # this suggests we hit some other page _resp["response"] = "unexpected content-type" # should be a roll-up status -- e.g. if we parse the fields and there aren't outages.. # otherwise this is pseudo-redundant of the status_code. else: """ # parse the html/text body of the response. caution: it's not valid xml. //html/body/div /div[contains(@class, "gradient")] /div[@id="current_events_block"] /div[contains(@class, "yui-content")] <<children>> <— table with tbody tr that we care about """ _rtxml = html_string(rt) _rtxml_regions_divs = _rtxml.xpath('//html/body/div/div[contains(@class, "gradient")]/div[@id="current_events_block"]/div[contains(@class, "yui-content")]') # could be more efficient, but good enough (searches through container div each time) # -> will give a list of divs _rtxml_regions_divs = _rtxml_regions_divs[0].xpath("div") region_NA = _rtxml_regions_divs[0].xpath("div[contains(@id, 'NA_block')]/table/tbody")[0] region_SA = _rtxml_regions_divs[0].xpath("div[contains(@id, 'SA_block')]/table/tbody")[0] region_EU = _rtxml_regions_divs[0].xpath("div[contains(@id, 'EU_block')]/table/tbody")[0] region_AP = _rtxml_regions_divs[0].xpath("div[contains(@id, 'AP_block')]/table/tbody")[0] region_table = { 'us': region_NA, 'sa': region_SA, 'eu': region_EU, 'ap': region_AP } lookup_regions = [] # At this point, we will only want to parse tables and make calls for the region the user requested if regions in ['all', '*'] or regions == ['*']: lookup_regions = ['us', 'sa', 'eu', 'ap'] #lookup_regions = [region_table['us'], region_table['sa'], region_table['eu'], region_table['ap']] #else: # for region in regions: # lookup_regions.append(region_table[region]) _response_dict = {} for r in lookup_regions: service_rows = region_table[r].getchildren() for row in service_rows: # get the tds _r = row.getchildren() # we're spot checking. parse the text instead of doing N calls to the RSS feeds. # useful if we drop into a database anyway with timestamp # [0] is status icon # the instance status can be strange if the status is "[RESOLVED] issue": # <td class="bb pad8"> # <div class="floatLeft">[RESOLVED] Instance Connectivity </div> # <a href="#" onclick="toggleCurrent(this, 'current_ec2-us-east-1_1452813122_NA');return(false);">more <img src="/images/more.gif"></a> # <div class="clear"></div> # <div id="current_ec2-us-east-1_1452813122_NA" style="display: none; margin-top: 8px;" class="yellowbg bordered-dark pad8"> # <div><span class="yellowfg"> 3:13 PM PST</span> We are investigating connectivity issues for some instances in the US-EAST-1 Region.</div><div><span class="yellowfg"> 3:33 PM PST</span> We can confirm connectivity issues when using public IP addresses for some instances within the EC2-Classic network in the US-EAST-1 Region. Connectivity between instances when using private IP addresses is not affected. We continue to work on resolution.</div><div><span class="yellowfg"> 4:00 PM PST</span> We continue to work on resolving the connectivity issues when using public IP addresses for some instances within the EC2-Classic network in the US-EAST-1 Region. For instances with an associated Elastic IP address (EIP), we have confirmed that re-associating the EIP address will restore connectivity. For instances using EC2 provided public IP addresses, associating a new EIP address will restore connectivity.</div><div><span class="yellowfg"> 6:19 PM PST</span> We continue to work on resolving public IP address connectivity for some EC2-Classic instances in the US-EAST-1 Region. We have started to see recovery for some of the affected instances and continue to work towards full recovery.</div><div><span class="yellowfg"> 7:11 PM PST</span> Between 2:26 PM and 7:10 PM PST we experienced connectivity issues when using public IP addresses for some instances within the EC2 Classic network in the US-EAST-1 Region. Connectivity between instances using the private IP address was not affected. The issue has been resolved and the service is operating normally.</div> # </div> # </td> try: _status = self.aws_current_status_lookup[_r[2].text] except KeyError: # probably "\n" try: _status = _r[2][0].text except IndexError: _status = "ERROR FETCHING STATUS" _response_dict[_r[1].text] = { 'detail': _r[2].text, 'rss': _r[3].getchildren()[0].items()[0][1], 'status': _status } # in case it's decidedly cleaner to use .update # _ret = {} # _ret['service'] = _r[1].text # _ret['detail'] = _r[2].text # _ret['rss'] = _r[3].getchildren()[0].items()[0][1] # _ret['status'] = self.aws_current_status_lookup[_ret['detail']] if self.follow_rss: pass # todo: async fire off requests to each # this definitely needs async handling. do not enable without. _resp["response"] = _response_dict return _resp else: # todo: parse var rt for useful info when we know what such an error looks like _resp["response"] = rt return _resp
def get_sendgrid_status(url="http://status.sendgrid.com"): r = http_requests_get_to_dict(url) _resp = {"response": dict(), "headers": dict(), "status": "ok"} _filtered_headers = { "date": r.headers.get("date", ""), "last-modified": r.headers.get("last-modified", ""), "etag": r.headers.get("etag", ""), "x-request-id": r.headers.get("x-request-id", ""), "x-statuspage-version": r.headers.get("x-statuspage-version", ""), "status_code": r.headers.get("status_code", 200) } _resp["headers"] = _filtered_headers # sendgrid uses nested service statuses _resp["response"]["services"] = dict() # not really into this as a list, but will adjust as needed _resp["response"]["outages"] = [] general_page_status_text = '' if not r.error and r.http_2xx: _rtxml = html_string(r.text) _body = _rtxml.xpath("//html/body")[0] _content_div = _body.xpath( "div[contains(@class, 'layout-content')]/div[@class='container']" )[0] # page structure changes when sendgrid is in an outage general_page_status = _content_div.xpath( "div[contains(@class, 'page-status')]/span[contains(@class, 'status')]" ) ongoing_incident_div = _content_div.xpath( "div[contains(@class, 'unresolved-incidents')]") if len(ongoing_incident_div) > 0: for _incident in ongoing_incident_div: _incident_title_div = _incident.xpath( "div[contains(@class, 'unresolved-incident')]/div[contains(@class, 'incident-title')]" ) _incident_text = _incident_title_div[0].xpath( "a[contains(@class, 'actual-title')]")[0].text.strip() _resp["response"]["outages"].append(_incident_text) if len(general_page_status) > 0: general_page_status_text = general_page_status[0].text.strip() if general_page_status_text == 'All Systems Operational': _resp["status"] = "ok" else: _resp["status"] = "nok" # sengrid does a .components-section .components-container, then N- .component-container (note singular) by_service_status_divs = _content_div.xpath( "div[contains(@class, 'components-section')]/div[contains(@class, 'components-container')]/div[contains(@class, 'component-container')]" ) _serv_status = {} for _service in by_service_status_divs: # we have to look into child-components container that is at the same level, e.g. # <div class="component-container"> # <div class="component-inner-container"> # <div class="child-components-container"> -> div.component-inner-container -> [span.name, span.component-status] # # if not, it's simply: # <div class="component-container"> # <div class="component-inner-container"> -> [span.name, span.component-status] _child_components_container = _service.xpath( "div[contains(@class, 'child-components-container')]") # if we have a child container, move our inspection to the scope of that div. otherwise, leave # at the top level for depth searching if len(_child_components_container) > 0: # step back to iter point, get name of the service header and make it into a dict for subitem statuses _service_name = _service.xpath( "div[contains(@class, 'component-inner-container')]/span[contains(@class, 'name')]/span" )[1].text.strip() _resp["response"]["services"][_service_name] = dict() # move our "cursor" to the child components container for searching _cursor = _child_components_container else: # we do not have a child components containiner, so keep the cursor at the loop iter point. _cursor = _service _service_component_containers = _cursor[0].xpath( "div[contains(@class, 'component-inner-container')]") for _service_status in _service_component_containers: _name = _service_status.xpath( "span[@class='name']")[0].text.strip() _status = _service_status.xpath( "span[@class='component-status']")[0].text.strip() if _status == "Operational": _status = "ok" _serv_status[_name] = _status _resp["response"]["services"] = _serv_status else: _resp["status"] = "nok" return _resp
def get_sendgrid_status(url="http://status.sendgrid.com"): r = http_requests_get_to_dict(url) _resp = { "response": dict(), "headers": dict(), "status": "ok" } _filtered_headers = { "date": r.headers.get("date", ""), "last-modified": r.headers.get("last-modified", ""), "etag": r.headers.get("etag", ""), "x-request-id": r.headers.get("x-request-id", ""), "x-statuspage-version": r.headers.get("x-statuspage-version", ""), "status_code": r.headers.get("status_code", 200) } _resp["headers"] = _filtered_headers # sendgrid uses nested service statuses _resp["response"]["services"] = dict() # not really into this as a list, but will adjust as needed _resp["response"]["outages"] = [] general_page_status_text = '' if not r.error and r.http_2xx: _rtxml = html_string(r.text) _body = _rtxml.xpath("//html/body")[0] _content_div = _body.xpath("div[contains(@class, 'layout-content')]/div[@class='container']")[0] # page structure changes when sendgrid is in an outage general_page_status = _content_div.xpath("div[contains(@class, 'page-status')]/span[contains(@class, 'status')]") ongoing_incident_div = _content_div.xpath("div[contains(@class, 'unresolved-incidents')]") if len(ongoing_incident_div) > 0: for _incident in ongoing_incident_div: _incident_title_div = _incident.xpath("div[contains(@class, 'unresolved-incident')]/div[contains(@class, 'incident-title')]") _incident_text = _incident_title_div[0].xpath("a[contains(@class, 'actual-title')]")[0].text.strip() _resp["response"]["outages"].append(_incident_text) if len(general_page_status) > 0: general_page_status_text = general_page_status[0].text.strip() if general_page_status_text == 'All Systems Operational': _resp["status"] = "ok" else: _resp["status"] = "nok" # sengrid does a .components-section .components-container, then N- .component-container (note singular) by_service_status_divs = _content_div.xpath("div[contains(@class, 'components-section')]/div[contains(@class, 'components-container')]/div[contains(@class, 'component-container')]") _serv_status = {} for _service in by_service_status_divs: # we have to look into child-components container that is at the same level, e.g. # <div class="component-container"> # <div class="component-inner-container"> # <div class="child-components-container"> -> div.component-inner-container -> [span.name, span.component-status] # # if not, it's simply: # <div class="component-container"> # <div class="component-inner-container"> -> [span.name, span.component-status] _child_components_container = _service.xpath("div[contains(@class, 'child-components-container')]") # if we have a child container, move our inspection to the scope of that div. otherwise, leave # at the top level for depth searching if len(_child_components_container) > 0: # step back to iter point, get name of the service header and make it into a dict for subitem statuses _service_name = _service.xpath("div[contains(@class, 'component-inner-container')]/span[contains(@class, 'name')]/span")[1].text.strip() _resp["response"]["services"][_service_name] = dict() # move our "cursor" to the child components container for searching _cursor = _child_components_container else: # we do not have a child components containiner, so keep the cursor at the loop iter point. _cursor = _service _service_component_containers = _cursor[0].xpath("div[contains(@class, 'component-inner-container')]") for _service_status in _service_component_containers: _name = _service_status.xpath("span[@class='name']")[0].text.strip() _status = _service_status.xpath("span[@class='component-status']")[0].text.strip() if _status == "Operational": _status = "ok" _serv_status[_name] = _status _resp["response"]["services"] = _serv_status else: _resp["status"] = "nok" return _resp
def get_aws_endpoint_status(self, regions=None): # todo: needs a "i only care about service x,y,z" if not regions: regions = self.regions r = requests.get(self.status_endpoint) # need to log the following types of errors and handle them # try: # r = requests.get(endpoint) # except requests.ConnectionError: # except requests.Timeout: # except requests.HTTPError: # except requests.TooManyRedirects: rc = r.status_code rt = r.text rheaders = r.headers _resp = {"response": {}, "headers": {}} _resp["headers"]["status_code"] = rc # not the same as date of response _resp["headers"]["last-modified"] = rheaders.get("last-modified", "") _resp["headers"]["etag"] = rheaders.get("etag", "") # todo: convert date to standard format or return "now" _resp["headers"]["date"] = rheaders.get("date", "") if str(rc).startswith("2"): # if we received an OK from Amazon, then continue processing if "json" in rheaders["content-type"]: # this would be unexpected -- amazon returns text/html for their status page. # this suggests we hit some other page _resp["response"] = "unexpected content-type" # should be a roll-up status -- e.g. if we parse the fields and there aren't outages.. # otherwise this is pseudo-redundant of the status_code. else: """ # parse the html/text body of the response. caution: it's not valid xml. //html/body/div /div[contains(@class, "gradient")] /div[@id="current_events_block"] /div[contains(@class, "yui-content")] <<children>> <— table with tbody tr that we care about """ _rtxml = html_string(rt) _rtxml_regions_divs = _rtxml.xpath( '//html/body/div/div[contains(@class, "gradient")]/div[@id="current_events_block"]/div[contains(@class, "yui-content")]' ) # could be more efficient, but good enough (searches through container div each time) # -> will give a list of divs _rtxml_regions_divs = _rtxml_regions_divs[0].xpath("div") region_NA = _rtxml_regions_divs[0].xpath( "div[contains(@id, 'NA_block')]/table/tbody")[0] region_SA = _rtxml_regions_divs[0].xpath( "div[contains(@id, 'SA_block')]/table/tbody")[0] region_EU = _rtxml_regions_divs[0].xpath( "div[contains(@id, 'EU_block')]/table/tbody")[0] region_AP = _rtxml_regions_divs[0].xpath( "div[contains(@id, 'AP_block')]/table/tbody")[0] region_table = { 'us': region_NA, 'sa': region_SA, 'eu': region_EU, 'ap': region_AP } lookup_regions = [] # At this point, we will only want to parse tables and make calls for the region the user requested if regions in ['all', '*'] or regions == ['*']: lookup_regions = ['us', 'sa', 'eu', 'ap'] #lookup_regions = [region_table['us'], region_table['sa'], region_table['eu'], region_table['ap']] #else: # for region in regions: # lookup_regions.append(region_table[region]) _response_dict = {} for r in lookup_regions: service_rows = region_table[r].getchildren() for row in service_rows: # get the tds _r = row.getchildren() # we're spot checking. parse the text instead of doing N calls to the RSS feeds. # useful if we drop into a database anyway with timestamp # [0] is status icon # the instance status can be strange if the status is "[RESOLVED] issue": # <td class="bb pad8"> # <div class="floatLeft">[RESOLVED] Instance Connectivity </div> # <a href="#" onclick="toggleCurrent(this, 'current_ec2-us-east-1_1452813122_NA');return(false);">more <img src="/images/more.gif"></a> # <div class="clear"></div> # <div id="current_ec2-us-east-1_1452813122_NA" style="display: none; margin-top: 8px;" class="yellowbg bordered-dark pad8"> # <div><span class="yellowfg"> 3:13 PM PST</span> We are investigating connectivity issues for some instances in the US-EAST-1 Region.</div><div><span class="yellowfg"> 3:33 PM PST</span> We can confirm connectivity issues when using public IP addresses for some instances within the EC2-Classic network in the US-EAST-1 Region. Connectivity between instances when using private IP addresses is not affected. We continue to work on resolution.</div><div><span class="yellowfg"> 4:00 PM PST</span> We continue to work on resolving the connectivity issues when using public IP addresses for some instances within the EC2-Classic network in the US-EAST-1 Region. For instances with an associated Elastic IP address (EIP), we have confirmed that re-associating the EIP address will restore connectivity. For instances using EC2 provided public IP addresses, associating a new EIP address will restore connectivity.</div><div><span class="yellowfg"> 6:19 PM PST</span> We continue to work on resolving public IP address connectivity for some EC2-Classic instances in the US-EAST-1 Region. We have started to see recovery for some of the affected instances and continue to work towards full recovery.</div><div><span class="yellowfg"> 7:11 PM PST</span> Between 2:26 PM and 7:10 PM PST we experienced connectivity issues when using public IP addresses for some instances within the EC2 Classic network in the US-EAST-1 Region. Connectivity between instances using the private IP address was not affected. The issue has been resolved and the service is operating normally.</div> # </div> # </td> try: _status = self.aws_current_status_lookup[ _r[2].text] except KeyError: # probably "\n" try: _status = _r[2][0].text except IndexError: _status = "ERROR FETCHING STATUS" _response_dict[_r[1].text] = { 'detail': _r[2].text, 'rss': _r[3].getchildren()[0].items()[0][1], 'status': _status } # in case it's decidedly cleaner to use .update # _ret = {} # _ret['service'] = _r[1].text # _ret['detail'] = _r[2].text # _ret['rss'] = _r[3].getchildren()[0].items()[0][1] # _ret['status'] = self.aws_current_status_lookup[_ret['detail']] if self.follow_rss: pass # todo: async fire off requests to each # this definitely needs async handling. do not enable without. _resp["response"] = _response_dict return _resp else: # todo: parse var rt for useful info when we know what such an error looks like _resp["response"] = rt return _resp
def get_cloudinary_status(url="http://status.cloudinary.com"): # this is only history -- no "we're fine right now" or enumeration of things that are working. # http://status.cloudinary.com/history.rss # also, as of this writing, there is no standard way to determine the end boundary of an outage, which # makes this generally useless for our purposes. r = http_requests_get_to_dict(url) # response dict - response is data blob to return (e.g. 'Site is normal!'), headers are headers, status is ok/nok _resp = { "response": dict(), "headers": dict(), "status": "ok" } _filtered_headers = { "date": r.headers.get("date", ""), "etag": r.headers.get("etag", ""), "status_code": r.headers.get("status_code", 200) } _resp["headers"] = _filtered_headers if not r.error and r.http_2xx: _rtxml = html_string(r.text) _body = _rtxml.xpath("//html/body")[0] _content_div = _body.xpath("div[contains(@class, 'layout-content')]/div[@class='container']")[0] general_page_status_raw_text = _content_div.xpath("div[contains(@class, 'page-status')]/span[contains(@class, 'status')]")[0].text general_page_status = general_page_status_raw_text.strip() by_service_status_divs = _content_div.xpath("div[contains(@class, 'components-section')]/div[contains(@class, 'components-container')]")[0] _serv_status = {} for _service in by_service_status_divs: _inner_div_group = _service.xpath("div[contains(@class, 'component-inner-container')]") if _inner_div_group: _inner_div_group = _inner_div_group[0] _name = _inner_div_group.xpath("span[@class='name']")[0].text.strip() _status = _inner_div_group.xpath("span[@class='component-status']")[0].text.strip() if _status == "Operational": _status = "ok" else: # statuspage.io nested group? # _service_div_contents = str(_service) _status = "There was an error parsing a response: %s : %s" % (str(_service), _inner_div_group) _serv_status[_name] = _status _resp["response"]["services"] = _serv_status if general_page_status == 'All Systems Operational': _resp["status"] = "ok" else: _resp["status"] = "nok" return _resp
def get_cloudinary_status(url="http://status.cloudinary.com"): # this is only history -- no "we're fine right now" or enumeration of things that are working. # http://status.cloudinary.com/history.rss # also, as of this writing, there is no standard way to determine the end boundary of an outage, which # makes this generally useless for our purposes. r = http_requests_get_to_dict(url) # response dict - response is data blob to return (e.g. 'Site is normal!'), headers are headers, status is ok/nok _resp = {"response": dict(), "headers": dict(), "status": "ok"} _filtered_headers = { "date": r.headers.get("date", ""), "etag": r.headers.get("etag", ""), "status_code": r.headers.get("status_code", 200) } _resp["headers"] = _filtered_headers if not r.error and r.http_2xx: _rtxml = html_string(r.text) _body = _rtxml.xpath("//html/body")[0] _content_div = _body.xpath( "div[contains(@class, 'layout-content')]/div[@class='container']" )[0] general_page_status_raw_text = _content_div.xpath( "div[contains(@class, 'page-status')]/span[contains(@class, 'status')]" )[0].text general_page_status = general_page_status_raw_text.strip() by_service_status_divs = _content_div.xpath( "div[contains(@class, 'components-section')]/div[contains(@class, 'components-container')]" )[0] _serv_status = {} for _service in by_service_status_divs: _inner_div_group = _service.xpath( "div[contains(@class, 'component-inner-container')]") if _inner_div_group: _inner_div_group = _inner_div_group[0] _name = _inner_div_group.xpath( "span[@class='name']")[0].text.strip() _status = _inner_div_group.xpath( "span[@class='component-status']")[0].text.strip() if _status == "Operational": _status = "ok" else: # statuspage.io nested group? # _service_div_contents = str(_service) _status = "There was an error parsing a response: %s : %s" % ( str(_service), _inner_div_group) _serv_status[_name] = _status _resp["response"]["services"] = _serv_status if general_page_status == 'All Systems Operational': _resp["status"] = "ok" else: _resp["status"] = "nok" return _resp