def test_task_not_in_progress(self): self.conn.post.return_value.status_code = 202 self.conn.post.return_value.headers = self.res_headers1.copy() self.conn.get.return_value.status_code = 201 self.conn.get.return_value.json.return_value = self.data.copy() res = self.conn.post(path='fake/path', data=self.data.copy(), headers=self.req_headers.copy()) tm = TaskMonitor(self.conn, res.headers.get('location'))\ .set_retry_after(res.headers.get('retry-after')) self.assertIsNotNone(tm) self.assertFalse(tm.in_progress)
def test_retry_after_http_date(self): self.conn.post.return_value.status_code = 202 self.conn.post.return_value.headers = self.res_headers1.copy() self.conn.get.return_value.status_code = 202 self.conn.get.return_value.headers = self.res_headers1.copy() self.conn.get.return_value.json.return_value = {} res = self.conn.post(path='fake/path', data=self.data.copy(), headers=self.req_headers.copy()) tm = TaskMonitor(self.conn, res.headers.get('location')) \ .set_retry_after(res.headers.get('retry-after')) self.assertIsNotNone(tm) self.assertEqual(self.datetime, tm.retry_after)
def test_retry_after_seconds(self): self.conn.post.return_value.status_code = 202 self.conn.post.return_value.headers = self.res_headers2.copy() self.conn.get.return_value.status_code = 202 self.conn.get.return_value.headers = self.res_headers2.copy() self.conn.get.return_value.json.return_value = {} start = datetime.now() + timedelta(seconds=self.seconds) res = self.conn.post(path='fake/path', data=self.data.copy(), headers=self.req_headers.copy()) tm = TaskMonitor(self.conn, res.headers.get('location')) \ .set_retry_after(res.headers.get('retry-after')) end = datetime.now() + timedelta(seconds=self.seconds) self.assertIsNotNone(tm) self.assertTrue(start <= tm.retry_after <= end)
def test_init_deprecation_warning(self, mock_log): self.conn.post.return_value.status_code = 202 self.conn.post.return_value.headers = self.res_headers1.copy() self.conn.get.return_value.status_code = 202 self.conn.get.return_value.headers = self.res_headers1.copy() self.conn.get.return_value.json.return_value = {} res = self.conn.post(path='fake/path', data=self.data.copy(), headers=self.req_headers.copy()) TaskMonitor(self.conn, res.headers.get('location'))\ .set_retry_after(res.headers.get('retry-after')) mock_log.assert_called_once_with( 'sushy.resources.task_monitor.TaskMonitor is deprecated. ' 'Use sushy.taskmonitor.TaskMonitor')
def test_sleep_for(self): self.conn.post.return_value.status_code = 202 self.conn.post.return_value.headers = self.res_headers2.copy() self.conn.get.return_value.status_code = 202 self.conn.get.return_value.headers = self.res_headers2.copy() self.conn.get.return_value.json.return_value = {} start = datetime.now() res = self.conn.post(path='fake/path', data=self.data.copy(), headers=self.req_headers.copy()) tm = TaskMonitor(self.conn, res.headers.get('location')) \ .set_retry_after(res.headers.get('retry-after')) self.assertIsNotNone(tm) sleep_for = tm.sleep_for elapsed = (datetime.now() - start).total_seconds() self.assertTrue(self.seconds - elapsed <= sleep_for <= self.seconds)
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