Files
python-cinderclient/cinderclient/tests/unit/test_shell.py
Mike Perez 4ea65b4409 Add version removal rule to stop discovery warning
Cinder recently added version discovery leveraging Keystone Client. For
cases where the service catalog still contains version numbers, this can
result in Keystone attempting to do discovery at the base url with the
version number and giving warnings. This will set a version removal rule
so Keystone can find the correct base url.

Change-Id: I71432468fea8bf1e50f180ab7f6dd69ee9aaa7e6
Closes-Bug: #1448244
2015-04-29 09:29:01 +10:00

369 lines
14 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import re
import sys
import fixtures
from keystoneclient import fixture as keystone_client_fixture
import mock
import requests_mock
from six import moves
from testtools import matchers
from cinderclient import exceptions
from cinderclient import shell
from cinderclient.tests.unit.fixture_data import base as fixture_base
from cinderclient.tests.unit.fixture_data import keystone_client
from cinderclient.tests.unit import utils
import keystoneclient.exceptions as ks_exc
from keystoneclient.exceptions import DiscoveryFailure
class ShellTest(utils.TestCase):
FAKE_ENV = {
'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': '%s/v2.0' % keystone_client.BASE_HOST,
}
# Patch os.environ to avoid required auth info.
def make_env(self, exclude=None):
env = dict((k, v) for k, v in self.FAKE_ENV.items() if k != exclude)
self.useFixture(fixtures.MonkeyPatch('os.environ', env))
def setUp(self):
super(ShellTest, self).setUp()
for var in self.FAKE_ENV:
self.useFixture(fixtures.EnvironmentVariable(var,
self.FAKE_ENV[var]))
def shell(self, argstr):
orig = sys.stdout
try:
sys.stdout = moves.StringIO()
_shell = shell.OpenStackCinderShell()
_shell.main(argstr.split())
except SystemExit:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.assertEqual(0, exc_value.code)
finally:
out = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = orig
return out
def test_help_unknown_command(self):
self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')
def test_help(self):
required = [
'.*?^usage: ',
'.*?(?m)^\s+create\s+Creates a volume.',
'.*?(?m)^Run "cinder help SUBCOMMAND" for help on a subcommand.',
]
help_text = self.shell('help')
for r in required:
self.assertThat(help_text,
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
def test_help_on_subcommand(self):
required = [
'.*?^usage: cinder list',
'.*?(?m)^Lists all volumes.',
]
help_text = self.shell('help list')
for r in required:
self.assertThat(help_text,
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
def register_keystone_auth_fixture(self, mocker, url):
mocker.register_uri('GET', url,
text=keystone_client.keystone_request_callback)
@requests_mock.Mocker()
def test_version_discovery(self, mocker):
_shell = shell.OpenStackCinderShell()
os_auth_url = "https://WrongDiscoveryResponse.discovery.com:35357/v2.0"
self.register_keystone_auth_fixture(mocker, os_auth_url)
self.assertRaises(DiscoveryFailure, _shell._discover_auth_versions,
None, auth_url=os_auth_url)
os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v2.0"
self.register_keystone_auth_fixture(mocker, os_auth_url)
v2_url, v3_url = _shell._discover_auth_versions(
None, auth_url=os_auth_url)
self.assertEqual(v2_url, os_auth_url, "Expected v2 url")
self.assertEqual(v3_url, None, "Expected no v3 url")
os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v3.0"
self.register_keystone_auth_fixture(mocker, os_auth_url)
v2_url, v3_url = _shell._discover_auth_versions(
None, auth_url=os_auth_url)
self.assertEqual(v3_url, os_auth_url, "Expected v3 url")
self.assertEqual(v2_url, None, "Expected no v2 url")
@requests_mock.Mocker()
def test_cinder_version_legacy_endpoint_v1_and_v2(self, mocker):
"""Verify that legacy endpoint settings still work.
Legacy endpoints that are not using version discovery is
<hostname>:<port>/<version>/(tenant_id)s. For this unit test, we fill
in the tenant_id for mocking purposes.
"""
token = keystone_client_fixture.V2Token()
cinder_url = 'http://127.0.0.1:8776/'
volume_service = token.add_service('volume', 'Cinder v1')
volume_service.add_endpoint(public=cinder_url, region='RegionOne')
volumev2_service = token.add_service('volumev2', 'Cinder v2')
volumev2_service.add_endpoint(public=cinder_url, region='RegionOne')
mocker.post(keystone_client.BASE_HOST + '/v2.0/tokens',
json=token)
mocker.get(cinder_url, json=fixture_base.generate_version_output())
volume_request = mocker.get('http://127.0.0.1:8776/v1/volumes/detail',
json={'volumes': {}})
self.shell('list')
self.assertTrue(volume_request.called)
@requests_mock.Mocker()
def test_cinder_version_legacy_endpoint_only_v1(self, mocker):
"""Verify that v1 legacy endpoint settings still work.
Legacy endpoints that are not using version discovery is
<hostname>:<port>/<version>/(tenant_id)s. For this unit test, we fill
in the tenant_id for mocking purposes.
"""
token = keystone_client_fixture.V2Token()
cinder_url = 'http://127.0.0.1:8776/'
volume_service = token.add_service('volume', 'Cinder v1')
volume_service.add_endpoint(
public=cinder_url,
region='RegionOne'
)
mocker.get(
cinder_url,
json=fixture_base.generate_version_output(v1=True, v2=False)
)
mocker.post(keystone_client.BASE_HOST + '/v2.0/tokens',
json=token)
volume_request = mocker.get('http://127.0.0.1:8776/v1/volumes/detail',
json={'volumes': {}})
self.shell('list')
self.assertTrue(volume_request.called)
@requests_mock.Mocker()
def test_cinder_version_legacy_endpoint_only_v2(self, mocker):
"""Verify that v2 legacy endpoint settings still work.
Legacy endpoints that are not using version discovery is
<hostname>:<port>/<version>/(tenant_id)s. For this unit test, we fill
in the tenant_id for mocking purposes.
"""
token = keystone_client_fixture.V2Token()
cinder_url = 'http://127.0.0.1:8776/'
volumev2_service = token.add_service('volumev2', 'Cinder v2')
volumev2_service.add_endpoint(
public=cinder_url,
region='RegionOne'
)
mocker.post(keystone_client.BASE_HOST + '/v2.0/tokens',
json=token)
mocker.get(
cinder_url,
json=fixture_base.generate_version_output(v1=False, v2=True)
)
volume_request = mocker.get('http://127.0.0.1:8776/v2/volumes/detail',
json={'volumes': {}})
self.shell('list')
self.assertTrue(volume_request.called)
@requests_mock.Mocker()
def test_cinder_version_discovery(self, mocker):
"""Verify client works two endpoints enabled under one service."""
token = keystone_client_fixture.V2Token()
volume_service = token.add_service('volume', 'Cinder')
volume_service.add_endpoint(public='http://127.0.0.1:8776',
region='RegionOne')
mocker.post(keystone_client.BASE_HOST + '/v2.0/tokens',
json=token)
mocker.get(
'http://127.0.0.1:8776/',
json=fixture_base.generate_version_output(v1=True, v2=True)
)
v1_request = mocker.get('http://127.0.0.1:8776/v1/volumes/detail',
json={'volumes': {}})
v2_request = mocker.get('http://127.0.0.1:8776/v2/volumes/detail',
json={'volumes': {}})
self.shell('list')
self.assertTrue(v1_request.called)
self.shell('--os-volume-api-version 2 list')
self.assertTrue(v2_request.called)
@requests_mock.Mocker()
def test_cinder_version_discovery_only_v1(self, mocker):
"""Verify when v1 is only enabled, the client discovers it."""
token = keystone_client_fixture.V2Token()
volume_service = token.add_service('volume', 'Cinder')
volume_service.add_endpoint(public='http://127.0.0.1:8776',
region='RegionOne')
mocker.post(keystone_client.BASE_HOST + '/v2.0/tokens',
json=token)
mocker.get(
'http://127.0.0.1:8776/',
json=fixture_base.generate_version_output(v1=True, v2=True)
)
volume_request = mocker.get('http://127.0.0.1:8776/v1/volumes/detail',
json={'volumes': {}})
self.shell('list')
self.assertTrue(volume_request.called)
@requests_mock.Mocker()
def test_cinder_version_discovery_only_v2(self, mocker):
"""Verify when v2 is enabled, the client discovers it."""
token = keystone_client_fixture.V2Token()
volumev2_service = token.add_service('volume', 'Cinder')
volumev2_service.add_endpoint(public='http://127.0.0.1:8776',
region='RegionOne')
mocker.post(keystone_client.BASE_HOST + '/v2.0/tokens',
json=token)
mocker.get(
'http://127.0.0.1:8776/',
json=fixture_base.generate_version_output(v1=False, v2=True)
)
volume_request = mocker.get('http://127.0.0.1:8776/v2/volumes/detail',
json={'volumes': {}})
self.shell('list')
self.assertTrue(volume_request.called)
@requests_mock.Mocker()
def test_cinder_version_discovery_fallback(self, mocker):
"""Client defaults to v1, but v2 is only available, fallback to v2."""
token = keystone_client_fixture.V2Token()
volumev2_service = token.add_service('volumev2', 'Cinder v2')
volumev2_service.add_endpoint(public='http://127.0.0.1:8776',
region='RegionOne')
mocker.post(keystone_client.BASE_HOST + '/v2.0/tokens',
json=token)
mocker.get(
'http://127.0.0.1:8776/',
json=fixture_base.generate_version_output(v1=False, v2=True)
)
volume_request = mocker.get('http://127.0.0.1:8776/v2/volumes/detail',
json={'volumes': {}})
self.shell('list')
self.assertTrue(volume_request.called)
@requests_mock.Mocker()
def test_cinder_version_discovery_unsupported_version(self, mocker):
"""Try a version from the client that's not enabled in Cinder."""
token = keystone_client_fixture.V2Token()
volume_service = token.add_service('volume', 'Cinder')
volume_service.add_endpoint(public='http://127.0.0.1:8776',
region='RegionOne')
mocker.post(keystone_client.BASE_HOST + '/v2.0/tokens',
json=token)
mocker.get(
'http://127.0.0.1:8776/',
json=fixture_base.generate_version_output(v1=False, v2=True)
)
self.assertRaises(exceptions.InvalidAPIVersion,
self.shell, '--os-volume-api-version 1 list')
@mock.patch('sys.stdin', side_effect=mock.MagicMock)
@mock.patch('getpass.getpass', return_value='password')
def test_password_prompted(self, mock_getpass, mock_stdin):
self.make_env(exclude='OS_PASSWORD')
# We should get a Connection Refused because there is no keystone.
self.assertRaises(ks_exc.ConnectionRefused, self.shell, 'list')
# Make sure we are actually prompted.
mock_getpass.assert_called_with('OS Password: ')
class CinderClientArgumentParserTest(utils.TestCase):
def test_ambiguity_solved_for_one_visible_argument(self):
parser = shell.CinderClientArgumentParser(add_help=False)
parser.add_argument('--test-parameter',
dest='visible_param',
action='store_true')
parser.add_argument('--test_parameter',
dest='hidden_param',
action='store_true',
help=argparse.SUPPRESS)
opts = parser.parse_args(['--test'])
# visible argument must be set
self.assertTrue(opts.visible_param)
self.assertFalse(opts.hidden_param)
def test_raise_ambiguity_error_two_visible_argument(self):
parser = shell.CinderClientArgumentParser(add_help=False)
parser.add_argument('--test-parameter',
dest="visible_param1",
action='store_true')
parser.add_argument('--test_parameter',
dest="visible_param2",
action='store_true')
self.assertRaises(SystemExit, parser.parse_args, ['--test'])
def test_raise_ambiguity_error_two_hidden_argument(self):
parser = shell.CinderClientArgumentParser(add_help=False)
parser.add_argument('--test-parameter',
dest="hidden_param1",
action='store_true',
help=argparse.SUPPRESS)
parser.add_argument('--test_parameter',
dest="hidden_param2",
action='store_true',
help=argparse.SUPPRESS)
self.assertRaises(SystemExit, parser.parse_args, ['--test'])