Use API v2 as default

Now we have claimed v2 is the current API version of Glance,
we should change the Glance client as well to be consistent
with Glance server.

DocImpact

Change-Id: I09c9e409d149e2d797785591183e06c13229b7f7
This commit is contained in:
Fei Long Wang 2015-06-22 15:47:48 +12:00
parent 9284eb4253
commit 181131ef2e
3 changed files with 107 additions and 29 deletions
glanceclient

@ -22,6 +22,7 @@ from __future__ import print_function
import argparse
import copy
import getpass
import hashlib
import json
import logging
import os
@ -263,7 +264,7 @@ class OpenStackImagesShell(object):
parser.add_argument('--os-image-api-version',
default=utils.env('OS_IMAGE_API_VERSION',
default=None),
help='Defaults to env[OS_IMAGE_API_VERSION] or 1.')
help='Defaults to env[OS_IMAGE_API_VERSION] or 2.')
parser.add_argument('--os_image_api_version',
help=argparse.SUPPRESS)
@ -551,9 +552,13 @@ class OpenStackImagesShell(object):
def _cache_schemas(self, options, home_dir='~/.glanceclient'):
homedir = os.path.expanduser(home_dir)
if not os.path.exists(homedir):
path_prefix = homedir
if options.os_auth_url:
hash_host = hashlib.sha1(options.os_auth_url.encode('utf-8'))
path_prefix = os.path.join(path_prefix, hash_host.hexdigest())
if not os.path.exists(path_prefix):
try:
os.makedirs(homedir)
os.makedirs(path_prefix)
except OSError as e:
# This avoids glanceclient to crash if it can't write to
# ~/.glanceclient, which may happen on some env (for me,
@ -561,12 +566,12 @@ class OpenStackImagesShell(object):
# /var/lib/jenkins).
msg = '%s' % e
print(encodeutils.safe_decode(msg), file=sys.stderr)
resources = ['image', 'metadefs/namespace', 'metadefs/resource_type']
schema_file_paths = [homedir + os.sep + x + '_schema.json'
schema_file_paths = [os.path.join(path_prefix, x + '_schema.json')
for x in ['image', 'namespace', 'resource_type']]
client = None
failed_download_schema = 0
for resource, schema_file_path in zip(resources, schema_file_paths):
if (not os.path.exists(schema_file_path)) or options.get_schema:
try:
@ -580,8 +585,11 @@ class OpenStackImagesShell(object):
except Exception:
# NOTE(esheffield) do nothing here, we'll get a message
# later if the schema is missing
failed_download_schema += 1
pass
return failed_download_schema >= len(resources)
def main(self, argv):
# Parse args once to find version
@ -605,7 +613,7 @@ class OpenStackImagesShell(object):
# build available subcommands based on version
try:
api_version = int(options.os_image_api_version or url_version or 1)
api_version = int(options.os_image_api_version or url_version or 2)
if api_version not in SUPPORTED_VERSIONS:
raise ValueError
except ValueError:
@ -614,7 +622,12 @@ class OpenStackImagesShell(object):
utils.exit(msg=msg)
if api_version == 2:
self._cache_schemas(options)
switch_version = self._cache_schemas(options)
if switch_version:
print('WARNING: The client is falling back to v1 because'
' the accessing to v2 failed. This behavior will'
' be removed in future versions')
api_version = 1
try:
subcommand_parser = self.get_subcommand_parser(api_version)

@ -24,26 +24,61 @@ class SimpleReadOnlyGlanceClientTest(base.ClientTestBase):
This only exercises client commands that are read only.
"""
def test_list(self):
out = self.glance('image-list')
def test_list_v1(self):
out = self.glance('--os-image-api-version 1 image-list')
endpoints = self.parser.listing(out)
self.assertTableStruct(endpoints, [
'ID', 'Name', 'Disk Format', 'Container Format',
'Size', 'Status'])
def test_list_v2(self):
out = self.glance('--os-image-api-version 2 image-list')
endpoints = self.parser.listing(out)
self.assertTableStruct(endpoints, ['ID', 'Name'])
def test_fake_action(self):
self.assertRaises(exceptions.CommandFailed,
self.glance,
'this-does-not-exist')
def test_member_list(self):
def test_member_list_v1(self):
tenant_name = '--tenant-id %s' % self.tenant_name
out = self.glance('member-list',
out = self.glance('--os-image-api-version 1 member-list',
params=tenant_name)
endpoints = self.parser.listing(out)
self.assertTableStruct(endpoints,
['Image ID', 'Member ID', 'Can Share'])
def test_member_list_v2(self):
try:
# NOTE(flwang): If set disk-format and container-format, Jenkins
# will raise an error said can't recognize the params, thouhg it
# works fine at local. Without the two params, Glance will
# complain. So we just catch the exception can skip it.
self.glance('--os-image-api-version 2 image-create --name temp')
except Exception:
pass
out = self.glance('--os-image-api-version 2 image-list'
' --visibility private')
image_list = self.parser.listing(out)
# NOTE(flwang): Because the member-list command of v2 is using
# image-id as required parameter, so we have to get a valid image id
# based on current environment. If there is no valid image id, we will
# pass in a fake one and expect a 404 error.
if len(image_list) > 0:
param_image_id = '--image-id %s' % image_list[0]['ID']
out = self.glance('--os-image-api-version 2 member-list',
params=param_image_id)
endpoints = self.parser.listing(out)
self.assertTableStruct(endpoints,
['Image ID', 'Member ID', 'Status'])
else:
param_image_id = '--image-id fake_image_id'
self.assertRaises(exceptions.CommandFailed,
self.glance,
'--os-image-api-version 2 member-list',
params=param_image_id)
def test_help(self):
help_text = self.glance('help')
lines = help_text.split('\n')

@ -19,6 +19,7 @@ try:
from collections import OrderedDict
except ImportError:
from ordereddict import OrderedDict
import hashlib
import os
import sys
import uuid
@ -197,8 +198,8 @@ class ShellTest(testutils.TestCase):
def test_no_auth_with_token_and_image_url_with_v1(self, v1_client):
# test no authentication is required if both token and endpoint url
# are specified
args = ('--os-auth-token mytoken --os-image-url https://image:1234/v1 '
'image-list')
args = ('--os-image-api-version 1 --os-auth-token mytoken'
' --os-image-url https://image:1234/v1 image-list')
glance_shell = openstack_shell.OpenStackImagesShell()
glance_shell.main(args.split())
assert v1_client.called
@ -206,7 +207,8 @@ class ShellTest(testutils.TestCase):
self.assertEqual('mytoken', kwargs['token'])
self.assertEqual('https://image:1234', args[0])
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas')
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
return_value=False)
def test_no_auth_with_token_and_image_url_with_v2(self,
cache_schemas):
with mock.patch('glanceclient.v2.client.Client') as v2_client:
@ -237,13 +239,14 @@ class ShellTest(testutils.TestCase):
@mock.patch('glanceclient.v1.client.Client')
def test_auth_plugin_invocation_with_v1(self, v1_client):
args = 'image-list'
args = '--os-image-api-version 1 image-list'
glance_shell = openstack_shell.OpenStackImagesShell()
glance_shell.main(args.split())
self._assert_auth_plugin_args()
@mock.patch('glanceclient.v2.client.Client')
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas')
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
return_value=False)
def test_auth_plugin_invocation_with_v2(self,
v2_client,
cache_schemas):
@ -255,13 +258,15 @@ class ShellTest(testutils.TestCase):
@mock.patch('glanceclient.v1.client.Client')
def test_auth_plugin_invocation_with_unversioned_auth_url_with_v1(
self, v1_client):
args = '--os-auth-url %s image-list' % DEFAULT_UNVERSIONED_AUTH_URL
args = ('--os-image-api-version 1 --os-auth-url %s image-list' %
DEFAULT_UNVERSIONED_AUTH_URL)
glance_shell = openstack_shell.OpenStackImagesShell()
glance_shell.main(args.split())
self._assert_auth_plugin_args()
@mock.patch('glanceclient.v2.client.Client')
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas')
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
return_value=False)
def test_auth_plugin_invocation_with_unversioned_auth_url_with_v2(
self, v2_client, cache_schemas):
args = ('--os-auth-url %s --os-image-api-version 2 '
@ -293,7 +298,8 @@ class ShellTest(testutils.TestCase):
@mock.patch(
'glanceclient.shell.OpenStackImagesShell._get_keystone_session')
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas')
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
return_value=False)
def test_no_auth_with_proj_name(self, cache_schemas, session):
with mock.patch('glanceclient.v2.client.Client'):
args = ('--os-project-name myname '
@ -407,7 +413,10 @@ class ShellTest(testutils.TestCase):
self.assertRaises(exc.CommandError, glance_shell.main, args.split())
@mock.patch('glanceclient.v2.client.Client')
def test_auth_plugin_invocation_without_tenant_with_v2(self, v2_client):
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
return_value=False)
def test_auth_plugin_invocation_without_tenant_with_v2(self, v2_client,
cache_schemas):
if 'OS_TENANT_NAME' in os.environ:
self.make_env(exclude='OS_TENANT_NAME')
if 'OS_PROJECT_ID' in os.environ:
@ -438,13 +447,14 @@ class ShellTestWithKeystoneV3Auth(ShellTest):
@mock.patch('glanceclient.v1.client.Client')
def test_auth_plugin_invocation_with_v1(self, v1_client):
args = 'image-list'
args = '--os-image-api-version 1 image-list'
glance_shell = openstack_shell.OpenStackImagesShell()
glance_shell.main(args.split())
self._assert_auth_plugin_args()
@mock.patch('glanceclient.v2.client.Client')
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas')
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
return_value=False)
def test_auth_plugin_invocation_with_v2(self, v2_client, cache_schemas):
args = '--os-image-api-version 2 image-list'
glance_shell = openstack_shell.OpenStackImagesShell()
@ -482,9 +492,12 @@ class ShellCacheSchemaTest(testutils.TestCase):
self._mock_client_setup()
self._mock_shell_setup()
self.cache_dir = '/dir_for_cached_schema'
self.cache_files = [self.cache_dir + '/image_schema.json',
self.cache_dir + '/namespace_schema.json',
self.cache_dir + '/resource_type_schema.json']
self.os_auth_url = 'http://localhost:5000/v2'
url_hex = hashlib.sha1(self.os_auth_url.encode('utf-8')).hexdigest()
self.prefix_path = (self.cache_dir + '/' + url_hex)
self.cache_files = [self.prefix_path + '/image_schema.json',
self.prefix_path + '/namespace_schema.json',
self.prefix_path + '/resource_type_schema.json']
def tearDown(self):
super(ShellCacheSchemaTest, self).tearDown()
@ -517,7 +530,8 @@ class ShellCacheSchemaTest(testutils.TestCase):
@mock.patch('os.path.exists', return_value=True)
def test_cache_schemas_gets_when_forced(self, exists_mock):
options = {
'get_schema': True
'get_schema': True,
'os_auth_url': self.os_auth_url
}
schema_odict = OrderedDict(self.schema_dict)
@ -538,7 +552,8 @@ class ShellCacheSchemaTest(testutils.TestCase):
@mock.patch('os.path.exists', side_effect=[True, False, False, False])
def test_cache_schemas_gets_when_not_exists(self, exists_mock):
options = {
'get_schema': False
'get_schema': False,
'os_auth_url': self.os_auth_url
}
schema_odict = OrderedDict(self.schema_dict)
@ -559,14 +574,29 @@ class ShellCacheSchemaTest(testutils.TestCase):
@mock.patch('os.path.exists', return_value=True)
def test_cache_schemas_leaves_when_present_not_forced(self, exists_mock):
options = {
'get_schema': False
'get_schema': False,
'os_auth_url': self.os_auth_url
}
self.shell._cache_schemas(self._make_args(options),
home_dir=self.cache_dir)
os.path.exists.assert_any_call(self.cache_dir)
os.path.exists.assert_any_call(self.prefix_path)
os.path.exists.assert_any_call(self.cache_files[0])
os.path.exists.assert_any_call(self.cache_files[1])
self.assertEqual(4, exists_mock.call_count)
self.assertEqual(0, open.mock_calls.__len__())
@mock.patch('six.moves.builtins.open', new=mock.mock_open(), create=True)
@mock.patch('os.path.exists', return_value=True)
def test_cache_schemas_leaves_auto_switch(self, exists_mock):
options = {
'get_schema': True,
'os_auth_url': self.os_auth_url
}
self.client.schemas.get.return_value = Exception()
switch_version = self.shell._cache_schemas(self._make_args(options),
home_dir=self.cache_dir)
self.assertEqual(switch_version, True)