Exemple #1
0
    def negotiate_version(self, conn, resp):
        """Negotiate the server version

        Assumption: Called after receiving a 406 error when doing a request.

        param conn: A connection object
        param resp: The response object from http request
        """
        if self.api_version_select_state not in API_VERSION_SELECTED_STATES:
            raise RuntimeError(
                'Error: self.api_version_select_state should be one of the '
                'values in: "%(valid)s" but had the value: "%(value)s"' %
                {'valid': ', '.join(API_VERSION_SELECTED_STATES),
                 'value': self.api_version_select_state})
        min_ver, max_ver = self._parse_version_headers(resp)
        # NOTE: servers before commit 32fb6e99 did not return version headers
        # on error, so we need to perform a GET to determine
        # the supported version range
        if not max_ver:
            LOG.debug('No version header in response, requesting from server')
            if self.os_ironic_api_version:
                base_version = ("/v%s" %
                                str(self.os_ironic_api_version).split('.')[0])
            else:
                base_version = API_VERSION
            resp = self._make_simple_request(conn, 'GET', base_version)
            min_ver, max_ver = self._parse_version_headers(resp)
        # If the user requested an explicit version or we have negotiated a
        # version and still failing then error now.  The server could
        # support the version requested but the requested operation may not
        # be supported by the requested version.
        if self.api_version_select_state == 'user':
            raise exc.UnsupportedVersion(textwrap.fill(
                "Requested API version %(req)s is not supported by the "
                "server or the requested operation is not supported by the "
                "requested version.  Supported version range is %(min)s to "
                "%(max)s"
                % {'req': self.os_ironic_api_version,
                   'min': min_ver, 'max': max_ver}))
        if self.api_version_select_state == 'negotiated':
            raise exc.UnsupportedVersion(textwrap.fill(
                "No API version was specified and the requested operation was "
                "not supported by the client's negotiated API version "
                "%(req)s.  Supported version range is: %(min)s to %(max)s"
                % {'req': self.os_ironic_api_version,
                   'min': min_ver, 'max': max_ver}))

        # TODO(deva): cache the negotiated version for this server
        negotiated_ver = min(self.os_ironic_api_version, max_ver)
        if negotiated_ver < min_ver:
            negotiated_ver = min_ver
        # server handles microversions, but doesn't support
        # the requested version, so try a negotiated version
        self.api_version_select_state = 'negotiated'
        self.os_ironic_api_version = negotiated_ver
        LOG.debug('Negotiated API version is %s', negotiated_ver)

        return negotiated_ver
Exemple #2
0
    def negotiate_version(self, conn, resp):
        """Negotiate the server version

        Assumption: Called after receiving a 406 error when doing a request.

        :param conn: A connection object
        :param resp: The response object from http request
        """
        def _query_server(conn):
            if (self.os_ironic_api_version
                    and not isinstance(self.os_ironic_api_version, list)
                    and self.os_ironic_api_version != 'latest'):
                base_version = ("/v%s" %
                                str(self.os_ironic_api_version).split('.')[0])
            else:
                base_version = API_VERSION
            return self._make_simple_request(conn, 'GET', base_version)

        version_overridden = False

        if (resp and hasattr(resp, 'request')
                and hasattr(resp.request, 'headers')):
            orig_hdr = resp.request.headers
            # Get the version of the client's last request and fallback
            # to the default for things like unit tests to not cause
            # migraines.
            req_api_ver = orig_hdr.get('X-OpenStack-Ironic-API-Version',
                                       self.os_ironic_api_version)
        else:
            req_api_ver = self.os_ironic_api_version
        if (resp and req_api_ver != self.os_ironic_api_version
                and self.api_version_select_state == 'negotiated'):
            # If we have a non-standard api version on the request,
            # but we think we've negotiated, then the call was overriden.
            # We should report the error with the called version
            requested_version = req_api_ver
            # And then we shouldn't save the newly negotiated
            # version of this negotiation because we have been
            # overridden a request.
            version_overridden = True
        else:
            requested_version = self.os_ironic_api_version

        if not resp:
            resp = _query_server(conn)
        if self.api_version_select_state not in API_VERSION_SELECTED_STATES:
            raise RuntimeError(
                _('Error: self.api_version_select_state should be one of the '
                  'values in: "%(valid)s" but had the value: "%(value)s"') % {
                      'valid': ', '.join(API_VERSION_SELECTED_STATES),
                      'value': self.api_version_select_state
                  })
        min_ver, max_ver = self._parse_version_headers(resp)
        # NOTE: servers before commit 32fb6e99 did not return version headers
        # on error, so we need to perform a GET to determine
        # the supported version range
        if not max_ver:
            LOG.debug('No version header in response, requesting from server')
            resp = _query_server(conn)
            min_ver, max_ver = self._parse_version_headers(resp)
        # Reset the maximum version that we permit
        if StrictVersion(max_ver) > StrictVersion(LATEST_VERSION):
            LOG.debug(
                "Remote API version %(max_ver)s is greater than the "
                "version supported by ironicclient. Maximum available "
                "version is %(client_ver)s", {
                    'max_ver': max_ver,
                    'client_ver': LATEST_VERSION
                })
            max_ver = LATEST_VERSION

        # If the user requested an explicit version or we have negotiated a
        # version and still failing then error now.  The server could
        # support the version requested but the requested operation may not
        # be supported by the requested version.
        # TODO(TheJulia): We should break this method into several parts,
        # such as a sanity check/error method.
        if ((self.api_version_select_state == 'user'
             and not self._must_negotiate_version())
                or (self.api_version_select_state == 'negotiated'
                    and version_overridden)):
            raise exc.UnsupportedVersion(
                textwrap.fill(
                    _("Requested API version %(req)s is not supported by the "
                      "server, client, or the requested operation is not "
                      "supported by the requested version. "
                      "Supported version range is %(min)s to "
                      "%(max)s") % {
                          'req': requested_version,
                          'min': min_ver,
                          'max': max_ver
                      }))
        if (self.api_version_select_state == 'negotiated'):
            raise exc.UnsupportedVersion(
                textwrap.fill(
                    _("No API version was specified or the requested operation "
                      "was not supported by the client's negotiated API version "
                      "%(req)s.  Supported version range is: %(min)s to %(max)s"
                      ) % {
                          'req': requested_version,
                          'min': min_ver,
                          'max': max_ver
                      }))

        if isinstance(requested_version, six.string_types):
            if requested_version == 'latest':
                negotiated_ver = max_ver
            else:
                negotiated_ver = str(
                    min(StrictVersion(requested_version),
                        StrictVersion(max_ver)))

        elif isinstance(requested_version, list):
            if 'latest' in requested_version:
                raise ValueError(
                    textwrap.fill(
                        _("The 'latest' API version can not be requested "
                          "in a list of versions. Please explicitly request "
                          "'latest' or request only versios between "
                          "%(min)s to %(max)s") % {
                              'min': min_ver,
                              'max': max_ver
                          }))

            versions = []
            for version in requested_version:
                if min_ver <= StrictVersion(version) <= max_ver:
                    versions.append(StrictVersion(version))
            if versions:
                negotiated_ver = str(max(versions))
            else:
                raise exc.UnsupportedVersion(
                    textwrap.fill(
                        _("Requested API version specified and the requested "
                          "operation was not supported by the client's "
                          "requested API version %(req)s.  Supported "
                          "version range is: %(min)s to %(max)s") % {
                              'req': requested_version,
                              'min': min_ver,
                              'max': max_ver
                          }))

        else:
            raise ValueError(
                textwrap.fill(
                    _("Requested API version %(req)s type is unsupported. "
                      "Valid types are Strings such as '1.1', 'latest' "
                      "or a list of string values representing API versions.")
                    % {'req': requested_version}))

        if StrictVersion(negotiated_ver) < StrictVersion(min_ver):
            negotiated_ver = min_ver
        # server handles microversions, but doesn't support
        # the requested version, so try a negotiated version
        self.api_version_select_state = 'negotiated'
        self.os_ironic_api_version = negotiated_ver
        LOG.debug('Negotiated API version is %s', negotiated_ver)

        # Cache the negotiated version for this server
        # TODO(vdrok): get rid of self.endpoint attribute in Stein
        endpoint_override = (getattr(self, 'endpoint_override', None)
                             or getattr(self, 'endpoint', None))
        host, port = get_server(endpoint_override)
        filecache.save_data(host=host, port=port, data=negotiated_ver)

        return negotiated_ver