Example #1
0
    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)
Example #2
0
    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
Example #3
0
    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
Example #4
0
    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
Example #5
0
    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
Example #6
0
    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