Fix authentication of Nova client
Blazar authenticates Nova requests with trust-scoped tokens. Sometime during the Ocata cycle these requests started failing (possibly due to stricter validation in Keystone) with the error: BadRequest: Invalid input for field 'identity/password/user/password': None is not of type 'string' (HTTP 400) This commit changes how the Nova client is configured to use the token_endpoint authentication plugin combined with endpoint_override, which allows to communicate with the Nova endpoint without extra requests to Keystone. This is necessary between trust-scoped tokens cannot re-authenticate with Keystone, which happens with other authentication plugins. Change-Id: Ibb6782140f41aea5e539e11f2618b3af2628fc4c Closes-Bug: #1660564
This commit is contained in:
parent
98d4c7ddf4
commit
81b85a11af
@ -13,6 +13,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
from keystoneauth1 import session
|
||||||
|
from keystoneauth1 import token_endpoint
|
||||||
from novaclient import client as nova_client
|
from novaclient import client as nova_client
|
||||||
|
|
||||||
from climate import context
|
from climate import context
|
||||||
@ -32,44 +34,41 @@ class TestCNClient(tests.TestCase):
|
|||||||
|
|
||||||
self.ctx = self.patch(self.context, 'current')
|
self.ctx = self.patch(self.context, 'current')
|
||||||
self.client = self.patch(self.n_client, 'Client')
|
self.client = self.patch(self.n_client, 'Client')
|
||||||
self.patch(self.base, 'url_for').return_value = 'http://fake.com/'
|
self.auth = self.patch(token_endpoint, 'Token')
|
||||||
|
self.session = self.patch(session, 'Session')
|
||||||
|
self.url = 'http://fake.com/'
|
||||||
|
self.patch(self.base, 'url_for').return_value = self.url
|
||||||
|
|
||||||
self.version = '2'
|
self.version = '2'
|
||||||
self.username = 'fake_user'
|
|
||||||
self.api_key = self.ctx().auth_token
|
|
||||||
self.project_id = self.ctx().project_id
|
|
||||||
self.auth_url = 'fake_auth'
|
|
||||||
self.mgmt_url = 'fake_mgmt'
|
|
||||||
|
|
||||||
def test_client_from_kwargs(self):
|
def test_client_from_kwargs(self):
|
||||||
self.ctx.side_effect = RuntimeError
|
self.ctx.side_effect = RuntimeError
|
||||||
|
self.auth_token = 'fake_token'
|
||||||
|
self.endpoint = 'fake_endpoint'
|
||||||
|
|
||||||
kwargs = {'version': self.version,
|
kwargs = {'version': self.version,
|
||||||
'username': self.username,
|
'endpoint_override': self.endpoint,
|
||||||
'api_key': self.api_key,
|
'auth_token': self.auth_token}
|
||||||
'project_id': self.project_id,
|
|
||||||
'auth_url': self.auth_url,
|
|
||||||
'mgmt_url': self.mgmt_url}
|
|
||||||
|
|
||||||
self.nova.ClimateNovaClient(**kwargs)
|
self.nova.ClimateNovaClient(**kwargs)
|
||||||
|
|
||||||
|
self.auth.assert_called_once_with(self.endpoint, self.auth_token)
|
||||||
|
self.session.assert_called_once_with(auth=self.auth.return_value)
|
||||||
self.client.assert_called_once_with(version=self.version,
|
self.client.assert_called_once_with(version=self.version,
|
||||||
username=self.username,
|
endpoint_override=self.endpoint,
|
||||||
api_key=self.api_key,
|
session=self.session.return_value)
|
||||||
project_id=self.project_id,
|
|
||||||
auth_url=self.auth_url)
|
|
||||||
|
|
||||||
def test_client_from_ctx(self):
|
def test_client_from_ctx(self):
|
||||||
|
|
||||||
kwargs = {'version': self.version}
|
kwargs = {'version': self.version}
|
||||||
|
|
||||||
self.nova.ClimateNovaClient(**kwargs)
|
self.nova.ClimateNovaClient(**kwargs)
|
||||||
|
|
||||||
|
self.auth.assert_called_once_with(self.url,
|
||||||
|
self.ctx().auth_token)
|
||||||
|
self.session.assert_called_once_with(auth=self.auth.return_value)
|
||||||
self.client.assert_called_once_with(version=self.version,
|
self.client.assert_called_once_with(version=self.version,
|
||||||
username=self.ctx().user_name,
|
endpoint_override=self.url,
|
||||||
api_key=None,
|
session=self.session.return_value)
|
||||||
project_id=self.ctx().project_id,
|
|
||||||
auth_url='http://fake.com/')
|
|
||||||
|
|
||||||
def test_getattr(self):
|
def test_getattr(self):
|
||||||
# TODO(n.s.): Will be done as soon as pypi package will be updated
|
# TODO(n.s.): Will be done as soon as pypi package will be updated
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
from keystoneauth1 import session
|
||||||
|
from keystoneauth1 import token_endpoint
|
||||||
from novaclient import client as nova_client
|
from novaclient import client as nova_client
|
||||||
from novaclient import exceptions as nova_exception
|
from novaclient import exceptions as nova_exception
|
||||||
from novaclient.v2 import servers
|
from novaclient.v2 import servers
|
||||||
@ -50,55 +52,37 @@ class ClimateNovaClient(object):
|
|||||||
:param version: service client version which we will use
|
:param version: service client version which we will use
|
||||||
:type version: str
|
:type version: str
|
||||||
|
|
||||||
:param username: username
|
|
||||||
:type username: str
|
|
||||||
|
|
||||||
:param api_key: password
|
|
||||||
:type api_key: str
|
|
||||||
|
|
||||||
:param auth_token: keystone auth token
|
:param auth_token: keystone auth token
|
||||||
:type auth_token: str
|
:type auth_token: str
|
||||||
|
|
||||||
:param project_id: project_id
|
:param endpoint_override: endpoint url which we will use
|
||||||
:type api_key: str
|
:type endpoint_override: str
|
||||||
|
|
||||||
:param auth_url: auth_url
|
|
||||||
:type auth_url: str
|
|
||||||
|
|
||||||
:param mgmt_url: management url
|
|
||||||
:type mgmt_url: str
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ctx = kwargs.pop('ctx', None)
|
ctx = kwargs.pop('ctx', None)
|
||||||
auth_token = kwargs.pop('auth_token', None)
|
auth_token = kwargs.pop('auth_token', None)
|
||||||
mgmt_url = kwargs.pop('mgmt_url', None)
|
endpoint_override = kwargs.pop('endpoint_override', None)
|
||||||
|
version = kwargs.pop('version', cfg.CONF.nova_client_version)
|
||||||
|
|
||||||
if ctx is None:
|
if ctx is None:
|
||||||
try:
|
try:
|
||||||
ctx = context.current()
|
ctx = context.current()
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
kwargs.setdefault('version', cfg.CONF.nova_client_version)
|
|
||||||
if ctx is not None:
|
if ctx is not None:
|
||||||
kwargs.setdefault('username', ctx.user_name)
|
|
||||||
kwargs.setdefault('api_key', None)
|
|
||||||
kwargs.setdefault('project_id', ctx.project_id)
|
|
||||||
kwargs.setdefault('auth_url', base.url_for(
|
|
||||||
ctx.service_catalog, CONF.identity_service))
|
|
||||||
|
|
||||||
auth_token = auth_token or ctx.auth_token
|
auth_token = auth_token or ctx.auth_token
|
||||||
mgmt_url = mgmt_url or base.url_for(ctx.service_catalog,
|
endpoint_override = endpoint_override or \
|
||||||
|
base.url_for(ctx.service_catalog,
|
||||||
CONF.compute_service)
|
CONF.compute_service)
|
||||||
if not kwargs.get('auth_url', None):
|
|
||||||
# NOTE(scroiset): novaclient v2.17.0 support only Identity API v2.0
|
|
||||||
auth_url = "%s://%s:%s/v2.0" % (CONF.os_auth_protocol,
|
|
||||||
CONF.os_auth_host,
|
|
||||||
CONF.os_auth_port)
|
|
||||||
kwargs['auth_url'] = auth_url
|
|
||||||
|
|
||||||
|
auth = token_endpoint.Token(endpoint_override,
|
||||||
|
auth_token)
|
||||||
|
sess = session.Session(auth=auth)
|
||||||
|
|
||||||
|
kwargs.setdefault('endpoint_override', endpoint_override)
|
||||||
|
kwargs.setdefault('session', sess)
|
||||||
|
kwargs.setdefault('version', version)
|
||||||
self.nova = nova_client.Client(**kwargs)
|
self.nova = nova_client.Client(**kwargs)
|
||||||
self.nova.client.auth_token = auth_token
|
|
||||||
self.nova.client.management_url = mgmt_url
|
|
||||||
|
|
||||||
self.nova.servers = ServerManager(self.nova)
|
self.nova.servers = ServerManager(self.nova)
|
||||||
|
|
||||||
@ -136,8 +120,5 @@ class NovaClientWrapper(object):
|
|||||||
@property
|
@property
|
||||||
def nova(self):
|
def nova(self):
|
||||||
ctx = context.current()
|
ctx = context.current()
|
||||||
nova = ClimateNovaClient(username=ctx.user_name,
|
nova = ClimateNovaClient(ctx=ctx)
|
||||||
api_key=None,
|
|
||||||
project_id=ctx.project_id,
|
|
||||||
ctx=ctx)
|
|
||||||
return nova
|
return nova
|
||||||
|
Loading…
Reference in New Issue
Block a user