Switch to keystoneauth

move cinderclient to keystoneauth as keystoneclient's auth session,
plugins and adapter code has been deprecated.

Co-Authored-By: Paulo Ewerton <pauloewerton@lsd.ufcg.edu.br>
Co-Authored-By: Sean McGinnis <sean.mcginnis@gmail.com>
Co-Authored-By: Jamie Lennox <jamielennox@gmail.com>

Change-Id: Id4bf0e2088e8ad99e83cd4f9b8549c2aca1f65a2
This commit is contained in:
Steve Martinelli 2016-03-09 07:57:04 -05:00 committed by Jamie Lennox
parent b61ec1e6a7
commit 16f83c4a53
8 changed files with 74 additions and 68 deletions

@ -30,10 +30,10 @@ import pkgutil
import re import re
import six import six
from keystoneclient import access from keystoneauth1 import access
from keystoneclient import adapter from keystoneauth1 import adapter
from keystoneclient.auth.identity import base from keystoneauth1.identity import base
from keystoneclient import discover from keystoneauth1 import discover
import requests import requests
from cinderclient import api_versions from cinderclient import api_versions
@ -109,7 +109,7 @@ class SessionClient(adapter.LegacyJsonAdapter):
api_versions.update_headers(kwargs["headers"], self.api_version) api_versions.update_headers(kwargs["headers"], self.api_version)
kwargs.setdefault('authenticated', False) kwargs.setdefault('authenticated', False)
# Note(tpatil): The standard call raises errors from # Note(tpatil): The standard call raises errors from
# keystoneclient, here we need to raise the cinderclient errors. # keystoneauth, here we need to raise the cinderclient errors.
raise_exc = kwargs.pop('raise_exc', True) raise_exc = kwargs.pop('raise_exc', True)
resp, body = super(SessionClient, self).request(*args, resp, body = super(SessionClient, self).request(*args,
raise_exc=False, raise_exc=False,
@ -427,7 +427,7 @@ class HTTPClient(object):
if resp.status_code == 200: # content must always present if resp.status_code == 200: # content must always present
try: try:
self.auth_url = url self.auth_url = url
self.auth_ref = access.AccessInfo.factory(resp, body) self.auth_ref = access.create(resp=resp, body=body)
self.service_catalog = self.auth_ref.service_catalog self.service_catalog = self.auth_ref.service_catalog
if extract_token: if extract_token:
@ -435,7 +435,7 @@ class HTTPClient(object):
management_url = self.service_catalog.url_for( management_url = self.service_catalog.url_for(
region_name=self.region_name, region_name=self.region_name,
endpoint_type=self.endpoint_type, interface=self.endpoint_type,
service_type=self.service_type, service_type=self.service_type,
service_name=self.service_name) service_name=self.service_name)
self.management_url = management_url.rstrip('/') self.management_url = management_url.rstrip('/')
@ -444,7 +444,10 @@ class HTTPClient(object):
print("Found more than one valid endpoint. Use a more " print("Found more than one valid endpoint. Use a more "
"restrictive filter") "restrictive filter")
raise raise
except KeyError: except ValueError:
# ValueError is raised when you pass an invalid response to
# access.create. This should never happen in reality if the
# status code is 200.
raise exceptions.AuthorizationFailure() raise exceptions.AuthorizationFailure()
except exceptions.EndpointNotFound: except exceptions.EndpointNotFound:
print("Could not find any suitable endpoint. Correct region?") print("Could not find any suitable endpoint. Correct region?")

@ -34,11 +34,12 @@ from cinderclient import utils
import cinderclient.auth_plugin import cinderclient.auth_plugin
from cinderclient._i18n import _ from cinderclient._i18n import _
from keystoneclient import discover from keystoneauth1 import discover
from keystoneclient import session from keystoneauth1 import loading
from keystoneclient.auth.identity import v2 as v2_auth from keystoneauth1 import session
from keystoneclient.auth.identity import v3 as v3_auth from keystoneauth1.identity import v2 as v2_auth
from keystoneclient.exceptions import DiscoveryFailure from keystoneauth1.identity import v3 as v3_auth
from keystoneauth1.exceptions import DiscoveryFailure
import six.moves.urllib.parse as urlparse import six.moves.urllib.parse as urlparse
from oslo_utils import encodeutils from oslo_utils import encodeutils
from oslo_utils import importutils from oslo_utils import importutils
@ -390,7 +391,7 @@ class OpenStackCinderShell(object):
help=argparse.SUPPRESS) help=argparse.SUPPRESS)
# Register the CLI arguments that have moved to the session object. # Register the CLI arguments that have moved to the session object.
session.Session.register_cli_options(parser) loading.register_session_argparse_arguments(parser)
parser.set_defaults(insecure=utils.env('CINDERCLIENT_INSECURE', parser.set_defaults(insecure=utils.env('CINDERCLIENT_INSECURE',
default=False)) default=False))
@ -468,9 +469,8 @@ class OpenStackCinderShell(object):
if hasattr(requests, 'logging'): if hasattr(requests, 'logging'):
requests.logging.getLogger(requests.__name__).addHandler(ch) requests.logging.getLogger(requests.__name__).addHandler(ch)
# required for logging when using a keystone session ks_logger = logging.getLogger("keystoneauth")
self.ks_logger = logging.getLogger("keystoneclient") ks_logger.setLevel(logging.DEBUG)
self.ks_logger.setLevel(logging.DEBUG)
def _delimit_metadata_args(self, argv): def _delimit_metadata_args(self, argv):
"""This function adds -- separator at the appropriate spot """This function adds -- separator at the appropriate spot
@ -788,7 +788,7 @@ class OpenStackCinderShell(object):
v2_auth_url = None v2_auth_url = None
v3_auth_url = None v3_auth_url = None
try: try:
ks_discover = discover.Discover(session=session, auth_url=auth_url) ks_discover = discover.Discover(session=session, url=auth_url)
v2_auth_url = ks_discover.url_for('2.0') v2_auth_url = ks_discover.url_for('2.0')
v3_auth_url = ks_discover.url_for('3.0') v3_auth_url = ks_discover.url_for('3.0')
except DiscoveryFailure: except DiscoveryFailure:

@ -10,7 +10,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from keystoneclient import fixture from keystoneauth1 import fixture
from cinderclient.tests.unit.fixture_data import base from cinderclient.tests.unit.fixture_data import base
from cinderclient.v1 import client as v1client from cinderclient.v1 import client as v1client

@ -11,19 +11,20 @@
# 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.
import logging
import json import json
import logging
import fixtures import fixtures
from keystoneauth1 import adapter
from keystoneauth1 import exceptions as keystone_exception
import mock import mock
import six
import cinderclient.client import cinderclient.client
import cinderclient.v1.client import cinderclient.v1.client
import cinderclient.v2.client import cinderclient.v2.client
from cinderclient import exceptions from cinderclient import exceptions
from cinderclient.tests.unit import utils from cinderclient.tests.unit import utils
from keystoneclient import adapter
from keystoneclient import exceptions as keystone_exception
class ClientTest(utils.TestCase): class ClientTest(utils.TestCase):
@ -115,7 +116,7 @@ class ClientTest(utils.TestCase):
mock_response = utils.TestResponse({ mock_response = utils.TestResponse({
"status_code": 202, "status_code": 202,
"text": json.dumps(resp), "text": six.b(json.dumps(resp)),
}) })
# 'request' method of Adaptor will return 202 response # 'request' method of Adaptor will return 202 response
@ -155,7 +156,7 @@ class ClientTest(utils.TestCase):
mock_response = utils.TestResponse({ mock_response = utils.TestResponse({
"status_code": 400, "status_code": 400,
"text": json.dumps(resp), "text": six.b(json.dumps(resp)),
}) })
# 'request' method of Adaptor will return 400 response # 'request' method of Adaptor will return 400 response
@ -182,7 +183,7 @@ class ClientTest(utils.TestCase):
mock_response = utils.TestResponse({ mock_response = utils.TestResponse({
"status_code": 413, "status_code": 413,
"text": json.dumps(resp), "text": six.b(json.dumps(resp)),
}) })
# 'request' method of Adaptor will return 413 response # 'request' method of Adaptor will return 413 response

@ -12,7 +12,6 @@
# limitations under the License. # limitations under the License.
import mock import mock
import requests import requests
from cinderclient import client from cinderclient import client
@ -24,18 +23,17 @@ fake_response = utils.TestResponse({
"status_code": 200, "status_code": 200,
"text": '{"hi": "there"}', "text": '{"hi": "there"}',
}) })
fake_response_empty = utils.TestResponse({
"status_code": 200,
"text": '{"access": {}}'
})
mock_request = mock.Mock(return_value=(fake_response)) mock_request = mock.Mock(return_value=(fake_response))
mock_request_empty = mock.Mock(return_value=(fake_response_empty))
refused_response = utils.TestResponse({
"status_code": 400,
"text": '[Errno 111] Connection refused',
})
refused_mock_request = mock.Mock(return_value=(refused_response))
bad_400_response = utils.TestResponse({ bad_400_response = utils.TestResponse({
"status_code": 400, "status_code": 400,
"text": '{"error": {"message": "n/a", "details": "Terrible!"}}', "text": '',
}) })
bad_400_request = mock.Mock(return_value=(bad_400_response)) bad_400_request = mock.Mock(return_value=(bad_400_response))
@ -74,6 +72,7 @@ def get_authed_client(retries=0):
cl = get_client(retries=retries) cl = get_client(retries=retries)
cl.management_url = "http://example.com" cl.management_url = "http://example.com"
cl.auth_token = "token" cl.auth_token = "token"
cl.get_service_url = mock.Mock(return_value="http://example.com")
return cl return cl
@ -287,24 +286,13 @@ class ClientTest(utils.TestCase):
cl = get_client() cl = get_client()
# response must not have x-server-management-url header # response must not have x-server-management-url header
@mock.patch.object(requests, "request", mock_request_empty) @mock.patch.object(requests, "request", mock_request)
def test_auth_call(): def test_auth_call():
self.assertRaises(exceptions.AuthorizationFailure, self.assertRaises(exceptions.AuthorizationFailure,
cl.authenticate) cl.authenticate)
test_auth_call() test_auth_call()
def test_auth_not_implemented(self):
cl = get_client()
# response must not have x-server-management-url header
# {'hi': 'there'} is neither V2 or V3
@mock.patch.object(requests, "request", mock_request)
def test_auth_call():
self.assertRaises(NotImplementedError, cl.authenticate)
test_auth_call()
def test_get_retry_timeout_error(self): def test_get_retry_timeout_error(self):
cl = get_authed_client(retries=1) cl = get_authed_client(retries=1)

@ -16,6 +16,9 @@ import re
import sys import sys
import fixtures import fixtures
import keystoneauth1.exceptions as ks_exc
from keystoneauth1.exceptions import DiscoveryFailure
from keystoneauth1 import session
import mock import mock
import pkg_resources import pkg_resources
import requests_mock import requests_mock
@ -31,8 +34,6 @@ from cinderclient.tests.unit.test_auth_plugins import mock_http_request
from cinderclient.tests.unit.test_auth_plugins import requested_headers from cinderclient.tests.unit.test_auth_plugins import requested_headers
from cinderclient.tests.unit.fixture_data import keystone_client from cinderclient.tests.unit.fixture_data import keystone_client
from cinderclient.tests.unit import utils from cinderclient.tests.unit import utils
import keystoneclient.exceptions as ks_exc
from keystoneclient.exceptions import DiscoveryFailure
class ShellTest(utils.TestCase): class ShellTest(utils.TestCase):
@ -103,23 +104,27 @@ class ShellTest(utils.TestCase):
@requests_mock.Mocker() @requests_mock.Mocker()
def test_version_discovery(self, mocker): def test_version_discovery(self, mocker):
_shell = shell.OpenStackCinderShell() _shell = shell.OpenStackCinderShell()
sess = session.Session()
os_auth_url = "https://WrongDiscoveryResponse.discovery.com:35357/v2.0" os_auth_url = "https://WrongDiscoveryResponse.discovery.com:35357/v2.0"
self.register_keystone_auth_fixture(mocker, os_auth_url) self.register_keystone_auth_fixture(mocker, os_auth_url)
self.assertRaises(DiscoveryFailure, _shell._discover_auth_versions,
None, auth_url=os_auth_url) self.assertRaises(DiscoveryFailure,
_shell._discover_auth_versions,
sess,
auth_url=os_auth_url)
os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v2.0" os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v2.0"
self.register_keystone_auth_fixture(mocker, os_auth_url) self.register_keystone_auth_fixture(mocker, os_auth_url)
v2_url, v3_url = _shell._discover_auth_versions( v2_url, v3_url = _shell._discover_auth_versions(sess,
None, auth_url=os_auth_url) auth_url=os_auth_url)
self.assertEqual(os_auth_url, v2_url, "Expected v2 url") self.assertEqual(os_auth_url, v2_url, "Expected v2 url")
self.assertIsNone(v3_url, "Expected no v3 url") self.assertIsNone(v3_url, "Expected no v3 url")
os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v3.0" os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v3.0"
self.register_keystone_auth_fixture(mocker, os_auth_url) self.register_keystone_auth_fixture(mocker, os_auth_url)
v2_url, v3_url = _shell._discover_auth_versions( v2_url, v3_url = _shell._discover_auth_versions(sess,
None, auth_url=os_auth_url) auth_url=os_auth_url)
self.assertEqual(os_auth_url, v3_url, "Expected v3 url") self.assertEqual(os_auth_url, v3_url, "Expected v3 url")
self.assertIsNone(v2_url, "Expected no v2 url") self.assertIsNone(v2_url, "Expected no v2 url")
@ -142,18 +147,18 @@ class ShellTest(utils.TestCase):
for count in range(1, 4): for count in range(1, 4):
self.list_volumes_on_service(count) self.list_volumes_on_service(count)
@mock.patch('keystoneclient.auth.identity.v2.Password') @mock.patch('keystoneauth1.identity.v2.Password')
@mock.patch('keystoneclient.adapter.Adapter.get_token', @mock.patch('keystoneauth1.adapter.Adapter.get_token',
side_effect=ks_exc.ConnectionRefused()) side_effect=ks_exc.ConnectFailure())
@mock.patch('keystoneclient.discover.Discover', @mock.patch('keystoneauth1.discover.Discover',
side_effect=ks_exc.ConnectionRefused()) side_effect=ks_exc.ConnectFailure())
@mock.patch('sys.stdin', side_effect=mock.MagicMock) @mock.patch('sys.stdin', side_effect=mock.MagicMock)
@mock.patch('getpass.getpass', return_value='password') @mock.patch('getpass.getpass', return_value='password')
def test_password_prompted(self, mock_getpass, mock_stdin, mock_discover, def test_password_prompted(self, mock_getpass, mock_stdin, mock_discover,
mock_token, mock_password): mock_token, mock_password):
self.make_env(exclude='OS_PASSWORD') self.make_env(exclude='OS_PASSWORD')
_shell = shell.OpenStackCinderShell() _shell = shell.OpenStackCinderShell()
self.assertRaises(ks_exc.ConnectionRefused, _shell.main, ['list']) self.assertRaises(ks_exc.ConnectFailure, _shell.main, ['list'])
mock_getpass.assert_called_with('OS Password: ') mock_getpass.assert_called_with('OS Password: ')
# Verify that Password() is called with value of param 'password' # Verify that Password() is called with value of param 'password'
# equal to mock_getpass.return_value. # equal to mock_getpass.return_value.
@ -223,7 +228,7 @@ class ShellTest(utils.TestCase):
self.assertEqual(False, _shell.cs.client.verify_cert) self.assertEqual(False, _shell.cs.client.verify_cert)
@mock.patch('keystoneclient.session.Session.__init__', @mock.patch('keystoneauth1.session.Session.__init__',
side_effect=RuntimeError()) side_effect=RuntimeError())
def test_http_client_with_cert(self, mock_session): def test_http_client_with_cert(self, mock_session):
_shell = shell.OpenStackCinderShell() _shell = shell.OpenStackCinderShell()
@ -234,7 +239,7 @@ class ShellTest(utils.TestCase):
self.assertRaises(RuntimeError, _shell.main, args) self.assertRaises(RuntimeError, _shell.main, args)
mock_session.assert_called_once_with(cert='minnie', verify=mock.ANY) mock_session.assert_called_once_with(cert='minnie', verify=mock.ANY)
@mock.patch('keystoneclient.session.Session.__init__', @mock.patch('keystoneauth1.session.Session.__init__',
side_effect=RuntimeError()) side_effect=RuntimeError())
def test_http_client_with_cert_and_key(self, mock_session): def test_http_client_with_cert_and_key(self, mock_session):
_shell = shell.OpenStackCinderShell() _shell = shell.OpenStackCinderShell()

@ -87,25 +87,34 @@ class FixturedTestCase(TestCase):
class TestResponse(requests.Response): class TestResponse(requests.Response):
"""Class used to wrap requests.Response and provide some """Class used to wrap requests.Response.
convenience to initialize with a dict.
Provides some convenience to initialize with a dict.
""" """
def __init__(self, data): def __init__(self, data):
super(TestResponse, self).__init__()
self._content = None
self._text = None self._text = None
super(TestResponse, self)
if isinstance(data, dict): if isinstance(data, dict):
self.status_code = data.get('status_code', None) self.status_code = data.get('status_code', None)
self.headers = data.get('headers', None) self.headers = data.get('headers', None)
self.reason = data.get('reason', '') self.reason = data.get('reason', '')
# Fake the text attribute to streamline Response creation # Fake text and content attributes to streamline Response creation
self._text = data.get('text', None) text = data.get('text', None)
self._content = text
self._text = text
else: else:
self.status_code = data self.status_code = data
def __eq__(self, other): def __eq__(self, other):
return self.__dict__ == other.__dict__ return self.__dict__ == other.__dict__
@property
def content(self):
return self._content
@property @property
def text(self): def text(self):
return self._text return self._text

@ -3,7 +3,7 @@
# process, which may cause wedges in the gate later. # process, which may cause wedges in the gate later.
pbr>=1.6 # Apache-2.0 pbr>=1.6 # Apache-2.0
PrettyTable<0.8,>=0.7 # BSD PrettyTable<0.8,>=0.7 # BSD
python-keystoneclient!=1.8.0,!=2.1.0,>=1.7.0 # Apache-2.0 keystoneauth1>=2.7.0 # Apache-2.0
requests>=2.10.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0
simplejson>=2.2.0 # MIT simplejson>=2.2.0 # MIT
Babel>=2.3.4 # BSD Babel>=2.3.4 # BSD