def wait(self, timeout_sec): """Waits until task is completed or it times out. :param timeout_sec: Timeout to wait :raises: ConnectionError when times out """ timeout_at = time.time() + timeout_sec while self.check_is_processing: LOG.debug( 'Waiting for task monitor %(url)s; sleeping for ' '%(sleep)s seconds', { 'url': self.task_monitor_uri, 'sleep': self.sleep_for }) time.sleep(self.sleep_for) if time.time() >= timeout_at and self.check_is_processing: m = ('Timeout waiting for task monitor %(url)s ' '(timeout = %(timeout)s)' % { 'url': self.task_monitor_uri, 'timeout': timeout_sec }) raise exceptions.ConnectionError(url=self.task_monitor_uri, error=m)
def _op(self, method, path='', data=None, headers=None): """Generic RESTful request handler. :param method: The HTTP method to be used, e.g: GET, POST, PUT, PATCH, etc... :param path: The sub-URI path to the resource. :param data: Optional JSON data. :param headers: Optional dictionary of headers. :returns: The response object from the requests library. :raises: ConnectionError :raises: HTTPError """ json_data = None if headers is None: headers = {} if data is not None: json_data = json.dumps(data) headers['Content-Type'] = 'application/json' url = parse.urljoin(self._url, path) # TODO(lucasagomes): We should mask the data to remove sensitive # information LOG.debug('HTTP request: %(method)s %(url)s; ' 'headers: %(headers)s; body: %(data)s', {'method': method, 'url': url, 'headers': headers, 'data': json_data}) try: response = self._session.request(method, url, data=json_data, headers=headers) except requests.ConnectionError as e: raise exceptions.ConnectionError(url=url, error=e) # If we received an AccessError, and we # previously established a redfish session # there is a chance that the session has timed-out. # Attempt to re-establish a session. try: exceptions.raise_for_response(method, url, response) except exceptions.AccessError: if self._auth.can_refresh_session(): self._auth.refresh_session() LOG.debug("Authentication refreshed successfully, " "retrying the call.") response = self._session.request(method, url, data=json_data, headers=headers) else: raise LOG.debug('HTTP response for %(method)s %(url)s: ' 'status code: %(code)s', {'method': method, 'url': url, 'code': response.status_code}) return response
def _op(self, method, path='', data=None, headers=None): """Generic RESTful request handler. :param method: The HTTP method to be used, e.g: GET, POST, PUT, PATCH, etc... :param path: The sub-URI path to the resource. :param data: Optional JSON data. :param headers: Optional dictionary of headers. :returns: The response object from the requests library. :raises: ConnectionError :raises: HTTPError """ if headers is None: headers = {} if data is not None: data = json.dumps(data) headers['Content-Type'] = 'application/json' url = parse.urljoin(self._url, path) # TODO(lucasagomes): We should mask the data to remove sensitive # information LOG.debug( 'HTTP request: %(method)s %(url)s; ' 'headers: %(headers)s; body: %(data)s', { 'method': method, 'url': url, 'headers': headers, 'data': data }) try: response = self._session.request(method, url, data=data, headers=headers) except requests.ConnectionError as e: raise exceptions.ConnectionError(url=url, error=e) exceptions.raise_for_response(method, url, response) LOG.debug( 'HTTP response for %(method)s %(url)s: ' 'status code: %(code)s', { 'method': method, 'url': url, 'code': response.status_code }) return response
def _op(self, method, path='', data=None, headers=None, blocking=False, timeout=60, **extra_session_req_kwargs): """Generic RESTful request handler. :param method: The HTTP method to be used, e.g: GET, POST, PUT, PATCH, etc... :param path: The sub-URI or absolute URL path to the resource. :param data: Optional JSON data. :param headers: Optional dictionary of headers. :param blocking: Whether to block for asynchronous operations. :param timeout: Max time in seconds to wait for blocking async call. :param extra_session_req_kwargs: Optional keyword argument to pass requests library arguments which would pass on to requests session object. :returns: The response object from the requests library. :raises: ConnectionError :raises: HTTPError """ url = path if urlparse.urlparse(path).netloc else urlparse.urljoin( self._url, path) headers = headers or {} if not any(k.lower() == 'odata-version' for k in headers): headers['OData-Version'] = '4.0' # TODO(lucasagomes): We should mask the data to remove sensitive # information LOG.debug('HTTP request: %(method)s %(url)s; headers: %(headers)s; ' 'body: %(data)s; blocking: %(blocking)s; timeout: ' '%(timeout)s; session arguments: %(session)s;', {'method': method, 'url': url, 'headers': headers, 'data': data, 'blocking': blocking, 'timeout': timeout, 'session': extra_session_req_kwargs}) try: response = self._session.request(method, url, json=data, headers=headers, **extra_session_req_kwargs) except requests.ConnectionError as e: raise exceptions.ConnectionError(url=url, error=e) if self._response_callback: self._response_callback(response) # If we received an AccessError, and we # previously established a redfish session # there is a chance that the session has timed-out. # Attempt to re-establish a session. try: exceptions.raise_for_response(method, url, response) except exceptions.AccessError: if self._auth.can_refresh_session(): self._auth.refresh_session() LOG.debug("Authentication refreshed successfully, " "retrying the call.") response = self._session.request(method, url, json=data, headers=headers, **extra_session_req_kwargs) else: raise if blocking and response.status_code == 202: if not response.headers.get('location'): m = ('HTTP response for %(method)s request to %(url)s ' 'returned status 202, but no Location header' % {'method': method, 'url': url}) raise exceptions.ConnectionError(url=url, error=m) timeout_at = time.time() + timeout mon = (TaskMonitor(self, response.headers.get('location')) .set_retry_after(response.headers.get('retry-after'))) while mon.in_progress: LOG.debug('Blocking for in-progress %(method)s call to ' '%(url)s; sleeping for %(sleep)s seconds', {'method': method, 'url': url, 'sleep': mon.sleep_for}) time.sleep(mon.sleep_for) if time.time() >= timeout_at and mon.in_progress: m = ('Timeout waiting for blocking %(method)s ' 'request to %(url)s (timeout = %(timeout)s)' % {'method': method, 'url': url, 'timeout': timeout}) raise exceptions.ConnectionError(url=url, error=m) response = mon.response LOG.debug('HTTP response for %(method)s %(url)s: ' 'status code: %(code)s', {'method': method, 'url': url, 'code': response.status_code}) return response
def _op(self, method, path='', data=None, headers=None, **extra_session_req_kwargs): """Generic RESTful request handler. :param method: The HTTP method to be used, e.g: GET, POST, PUT, PATCH, etc... :param path: The sub-URI path to the resource. :param data: Optional JSON data. :param headers: Optional dictionary of headers. :param extra_session_req_kwargs: Optional keyword argument to pass requests library arguments which would pass on to requests session object. :returns: The response object from the requests library. :raises: ConnectionError :raises: HTTPError """ url = parse.urljoin(self._url, path) headers = headers or {} if not any(k.lower() == 'odata-version' for k in headers): headers['OData-Version'] = '4.0' # TODO(lucasagomes): We should mask the data to remove sensitive # information LOG.debug( 'HTTP request: %(method)s %(url)s; headers: %(headers)s; ' 'body: %(data)s; session arguments: %(session)s;', { 'method': method, 'url': url, 'headers': headers, 'data': data, 'session': extra_session_req_kwargs }) try: response = self._session.request(method, url, json=data, headers=headers, **extra_session_req_kwargs) except requests.ConnectionError as e: raise exceptions.ConnectionError(url=url, error=e) # If we received an AccessError, and we # previously established a redfish session # there is a chance that the session has timed-out. # Attempt to re-establish a session. try: exceptions.raise_for_response(method, url, response) except exceptions.AccessError: if self._auth.can_refresh_session(): self._auth.refresh_session() LOG.debug("Authentication refreshed successfully, " "retrying the call.") response = self._session.request(method, url, json=data, headers=headers, **extra_session_req_kwargs) else: raise LOG.debug( 'HTTP response for %(method)s %(url)s: ' 'status code: %(code)s', { 'method': method, 'url': url, 'code': response.status_code }) return response
def _op(self, method, path='', data=None, headers=None, blocking=False, timeout=60, server_side_retries=_SERVER_SIDE_RETRIES, **extra_session_req_kwargs): """Generic RESTful request handler. :param method: The HTTP method to be used, e.g: GET, POST, PUT, PATCH, etc... :param path: The sub-URI or absolute URL path to the resource. :param data: Optional JSON data. :param headers: Optional dictionary of headers. :param blocking: Whether to block for asynchronous operations. :param timeout: Max time in seconds to wait for blocking async call. :param extra_session_req_kwargs: Optional keyword argument to pass requests library arguments which would pass on to requests session object. :returns: The response object from the requests library. :raises: ConnectionError :raises: HTTPError """ url = path if urlparse.urlparse(path).netloc else urlparse.urljoin( self._url, path) headers = headers or {} lc_headers = [k.lower() for k in headers] if data is not None and 'content-type' not in lc_headers: headers['Content-Type'] = 'application/json' if 'odata-version' not in lc_headers: headers['OData-Version'] = '4.0' # TODO(lucasagomes): We should mask the data to remove sensitive # information LOG.debug( 'HTTP request: %(method)s %(url)s; headers: %(headers)s; ' 'body: %(data)s; blocking: %(blocking)s; timeout: ' '%(timeout)s; session arguments: %(session)s;', { 'method': method, 'url': url, 'headers': utils.sanitize(headers), 'data': utils.sanitize(data), 'blocking': blocking, 'timeout': timeout, 'session': extra_session_req_kwargs }) try: response = self._session.request(method, url, json=data, headers=headers, **extra_session_req_kwargs) except requests.ConnectionError as e: raise exceptions.ConnectionError(url=url, error=e) if self._response_callback: self._response_callback(response) # If we received an AccessError, and we # previously established a redfish session # there is a chance that the session has timed-out. # Attempt to re-establish a session. try: exceptions.raise_for_response(method, url, response) except exceptions.AccessError as e: if self._auth.can_refresh_session(): try: self._auth.refresh_session() except exceptions.AccessError as refresh_exc: LOG.error( "A failure occured while attempting to refresh " "the session. Error: %s", refresh_exc.message) raise LOG.debug("Authentication refreshed successfully, " "retrying the call.") try: response = self._session.request( method, url, json=data, headers=headers, **extra_session_req_kwargs) except exceptions.HTTPError as retry_exc: LOG.error( "Failure occured while attempting to retry " "request after refreshing the session. Error: " "%s", retry_exc.message) raise else: if method == 'GET' and url.endswith('SessionService'): LOG.debug( 'HTTP GET of SessionService failed %s, ' 'this is expected prior to authentication', e.message) else: LOG.error( "Authentication error detected. Cannot proceed: " "%s", e.message) raise except exceptions.ServerSideError as e: if method.lower() != 'get' or server_side_retries <= 0: raise else: LOG.warning( 'Got server side error %s in response to a ' 'GET request, retrying after %d seconds', e, _SERVER_SIDE_RETRY_DELAY) time.sleep(_SERVER_SIDE_RETRY_DELAY) return self._op(method, path, data=data, headers=headers, blocking=blocking, timeout=timeout, server_side_retries=server_side_retries - 1, **extra_session_req_kwargs) if blocking and response.status_code == 202: if not response.headers.get('Location'): m = ('HTTP response for %(method)s request to %(url)s ' 'returned status 202, but no Location header' % { 'method': method, 'url': url }) raise exceptions.ConnectionError(url=url, error=m) mon = TaskMonitor.from_response(self, response, path) mon.wait(timeout) response = mon.response exceptions.raise_for_response(method, url, response) LOG.debug( 'HTTP response for %(method)s %(url)s: ' 'status code: %(code)s', { 'method': method, 'url': url, 'code': response.status_code }) return response