def validate_response(cls, schema, resp, body): # Only check the response if the status code is a success code # TODO(cyeoh): Eventually we should be able to verify that a failure # code if it exists is something that we expect. This is explicitly # declared in the V3 API and so we should be able to export this in # the response schema. For now we'll ignore it. return if resp.status in HTTP_SUCCESS + HTTP_REDIRECTION: cls.expected_success(schema['status_code'], resp.status) # Check the body of a response body_schema = schema.get('response_body') if body_schema: try: jsonschema.validate(body, body_schema, cls=JSONSCHEMA_VALIDATOR, format_checker=FORMAT_CHECKER) except jsonschema.ValidationError as ex: msg = ("HTTP response body is invalid (%s)") % ex raise exceptions.InvalidHTTPResponseBody(msg) else: if body: msg = ("HTTP response body should not exist (%s)") % body raise exceptions.InvalidHTTPResponseBody(msg) # Check the header of a response header_schema = schema.get('response_header') if header_schema: try: jsonschema.validate(resp, header_schema, cls=JSONSCHEMA_VALIDATOR, format_checker=FORMAT_CHECKER) except jsonschema.ValidationError as ex: msg = ("HTTP response header is invalid (%s)") % ex raise exceptions.InvalidHTTPResponseHeader(msg)
def _error_checker(self, method, url, headers, body, resp, resp_body): # NOTE(mtreinish): Check for httplib response from glance_http. The # object can't be used here because importing httplib breaks httplib2. # If another object from a class not imported were passed here as # resp this could possibly fail if str(type(resp)) == "<type 'instance'>": ctype = resp.getheader('content-type') else: try: ctype = resp['content-type'] # NOTE(mtreinish): Keystone delete user responses doesn't have a # content-type header. (They don't have a body) So just pretend it # is set. except KeyError: ctype = 'application/json' # It is not an error response if resp.status < 400: return JSON_ENC = ['application/json', 'application/json; charset=utf-8'] # NOTE(mtreinish): This is for compatibility with Glance and swift # APIs. These are the return content types that Glance api v1 # (and occasionally swift) are using. TXT_ENC = [ 'text/plain', 'text/html', 'text/html; charset=utf-8', 'text/plain; charset=utf-8' ] if ctype.lower() in JSON_ENC: parse_resp = True elif ctype.lower() in TXT_ENC: parse_resp = False else: raise exceptions.UnexpectedContentType(str(resp.status), resp=resp) if resp.status == 401: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.Unauthorized(resp_body, resp=resp) if resp.status == 403: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.Forbidden(resp_body, resp=resp) if resp.status == 404: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.NotFound(resp_body, resp=resp) if resp.status == 400: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.BadRequest(resp_body, resp=resp) if resp.status == 410: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.Gone(resp_body, resp=resp) if resp.status == 409: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.Conflict(resp_body, resp=resp) if resp.status == 413: if parse_resp: resp_body = self._parse_resp(resp_body) if self.is_absolute_limit(resp, resp_body): raise exceptions.OverLimit(resp_body, resp=resp) else: raise exceptions.RateLimitExceeded(resp_body, resp=resp) if resp.status == 415: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.InvalidContentType(resp_body, resp=resp) if resp.status == 422: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.UnprocessableEntity(resp_body, resp=resp) if resp.status in (500, 501): message = resp_body if parse_resp: try: resp_body = self._parse_resp(resp_body) except ValueError: # If response body is a non-json string message. # Use resp_body as is and raise InvalidResponseBody # exception. raise exceptions.InvalidHTTPResponseBody(message) else: if isinstance(resp_body, dict): # I'm seeing both computeFault # and cloudServersFault come back. # Will file a bug to fix, but leave as is for now. if 'cloudServersFault' in resp_body: message = resp_body['cloudServersFault']['message'] elif 'computeFault' in resp_body: message = resp_body['computeFault']['message'] elif 'error' in resp_body: message = resp_body['error']['message'] elif 'message' in resp_body: message = resp_body['message'] else: message = resp_body if resp.status == 501: raise exceptions.NotImplemented(resp_body, resp=resp, message=message) else: raise exceptions.ServerFault(resp_body, resp=resp, message=message) if resp.status >= 400: raise exceptions.UnexpectedResponseCode(str(resp.status), resp=resp)
def validate_URL_response(URL, expected_status_code=200, expected_body=None, HTTPS_verify=True, client_cert_path=None, CA_certs_path=None, request_interval=CONF.load_balancer.build_interval, request_timeout=CONF.load_balancer.build_timeout): """Check a URL response (HTTP or HTTPS). :param URL: The URL to query. :param expected_status_code: The expected HTTP status code. :param expected_body: The expected response text, None will not compare. :param HTTPS_verify: Should we verify the HTTPS server. :param client_cert_path: Filesystem path to a file with the client private key and certificate. :param CA_certs_path: Filesystem path to a file containing CA certificates to use for HTTPS validation. :param request_interval: Time, in seconds, to timeout a request. :param request_timeout: The maximum time, in seconds, to attempt requests. Failed validation of expected results does not result in a retry. :raises InvalidHttpSuccessCode: The expected_status_code did not match. :raises InvalidHTTPResponseBody: The response body did not match the expected content. :raises TimeoutException: The request timed out. :returns: None """ with requests.Session() as session: session_kwargs = {} if not HTTPS_verify: session_kwargs['verify'] = False if CA_certs_path: session_kwargs['verify'] = CA_certs_path if client_cert_path: session_kwargs['cert'] = client_cert_path session_kwargs['timeout'] = request_interval start = time.time() while time.time() - start < request_timeout: try: response = session.get(URL, **session_kwargs) if response.status_code != expected_status_code: raise exceptions.InvalidHttpSuccessCode( '{0} is not the expected code {1}'.format( response.status_code, expected_status_code)) if expected_body and response.text != expected_body: details = '{} does not match expected {}'.format( response.text, expected_body) raise exceptions.InvalidHTTPResponseBody(resp_body=details) return except requests.exceptions.Timeout: # Don't sleep as we have already waited the interval. LOG.info('Request for {} timed out. Retrying.'.format(URL)) except (exceptions.InvalidHttpSuccessCode, exceptions.InvalidHTTPResponseBody, requests.exceptions.SSLError): raise except Exception as e: LOG.info('Validate URL got exception: {0}. ' 'Retrying.'.format(e)) time.sleep(request_interval) raise exceptions.TimeoutException()