Fix 'List' command filters do not accept unicode symbols
When using the name or description to filter the share list, share group list, share network list or snapshot list, if the name or description is unicode symbols, the query will fail due to the invalid str value. This change is to fix them. Change-Id: I73ed2b675542d3aeedc9efdba08067819bf7d3e4 Closes-bug: #1712988
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@@ -170,3 +171,23 @@ class ShareNetworksReadWriteTest(base.BaseTestCase):
|
||||
filters=filters)
|
||||
|
||||
self.assertGreater(len(share_networks), 0)
|
||||
|
||||
def test_list_share_networks_by_inexact_unicode_option(self):
|
||||
self.create_share_network(
|
||||
name=u'网络名称',
|
||||
description=u'网络描述',
|
||||
neutron_net_id='fake_neutron_net_id',
|
||||
neutron_subnet_id='fake_neutron_subnet_id',
|
||||
)
|
||||
|
||||
filters = {'name~': u'名称'}
|
||||
share_networks = self.admin_client.list_share_networks(
|
||||
filters=filters)
|
||||
|
||||
self.assertGreater(len(share_networks), 0)
|
||||
|
||||
filters = {'description~': u'描述'}
|
||||
share_networks = self.admin_client.list_share_networks(
|
||||
filters=filters)
|
||||
|
||||
self.assertGreater(len(share_networks), 0)
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@@ -311,6 +312,21 @@ class SharesListReadWriteTest(base.BaseTestCase):
|
||||
self.assertTrue(
|
||||
any(self.private_share['id'] == s['ID'] for s in shares))
|
||||
|
||||
def test_list_shares_by_inexact_unicode_option(self):
|
||||
self.create_share(
|
||||
name=u'共享名称',
|
||||
description=u'共享描述',
|
||||
public=True,
|
||||
client=self.get_user_client(),
|
||||
cleanup_in_class=True)
|
||||
filters = {'name~': u'名称'}
|
||||
shares = self.user_client.list_shares(filters=filters)
|
||||
self.assertGreater(len(shares), 0)
|
||||
|
||||
filters = {'description~': u'描述'}
|
||||
shares = self.user_client.list_shares(filters=filters)
|
||||
self.assertGreater(len(shares), 0)
|
||||
|
||||
def test_list_shares_by_description(self):
|
||||
shares = self.user_client.list_shares(
|
||||
filters={'description': self.private_description})
|
||||
|
29
manilaclient/tests/unit/test_utils.py
Normal file
29
manilaclient/tests/unit/test_utils.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# 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 six
|
||||
import testtools
|
||||
|
||||
from manilaclient import utils
|
||||
|
||||
|
||||
class TestCommonUtils(testtools.TestCase):
|
||||
|
||||
def test_unicode_key_value_to_string(self):
|
||||
src = {u'key': u'\u70fd\u7231\u5a77'}
|
||||
expected = {'key': '\xe7\x83\xbd\xe7\x88\xb1\xe5\xa9\xb7'}
|
||||
if six.PY2:
|
||||
self.assertEqual(expected, utils.unicode_key_value_to_string(src))
|
||||
else:
|
||||
# u'xxxx' in PY3 is str, we will not get extra 'u' from cli
|
||||
# output in PY3
|
||||
self.assertEqual(src, utils.unicode_key_value_to_string(src))
|
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# Copyright 2014 Mirantis, Inc.
|
||||
# All Rights Reserved.
|
||||
@@ -261,6 +262,22 @@ class ShellTest(test_utils.TestCase):
|
||||
'GET',
|
||||
'/shares/detail?description%7E=fake_description')
|
||||
|
||||
def test_list_filter_by_inexact_unicode_name(self):
|
||||
for separator in self.separators:
|
||||
self.run_command('list --name~' + separator +
|
||||
u'ффф')
|
||||
self.assert_called(
|
||||
'GET',
|
||||
'/shares/detail?name%7E=%D1%84%D1%84%D1%84')
|
||||
|
||||
def test_list_filter_by_inexact_unicode_description(self):
|
||||
for separator in self.separators:
|
||||
self.run_command('list --description~' + separator +
|
||||
u'ффф')
|
||||
self.assert_called(
|
||||
'GET',
|
||||
'/shares/detail?description%7E=%D1%84%D1%84%D1%84')
|
||||
|
||||
def test_list_filter_by_share_type_not_found(self):
|
||||
for separator in self.separators:
|
||||
self.assertRaises(
|
||||
@@ -800,6 +817,22 @@ class ShellTest(test_utils.TestCase):
|
||||
'GET',
|
||||
'/snapshots/detail?description%7E=fake_description')
|
||||
|
||||
def test_list_snapshots_filter_by_inexact_unicode_name(self):
|
||||
for separator in self.separators:
|
||||
self.run_command('snapshot-list --name~' + separator +
|
||||
u'ффф')
|
||||
self.assert_called(
|
||||
'GET',
|
||||
'/snapshots/detail?name%7E=%D1%84%D1%84%D1%84')
|
||||
|
||||
def test_list_snapshots_filter_by_inexact_unicode_description(self):
|
||||
for separator in self.separators:
|
||||
self.run_command('snapshot-list --description~' + separator +
|
||||
u'ффф')
|
||||
self.assert_called(
|
||||
'GET',
|
||||
'/snapshots/detail?description%7E=%D1%84%D1%84%D1%84')
|
||||
|
||||
def test_list_snapshots_with_sort_dir_verify_keys(self):
|
||||
aliases = ['--sort_dir', '--sort-dir']
|
||||
for alias in aliases:
|
||||
@@ -1460,6 +1493,22 @@ class ShellTest(test_utils.TestCase):
|
||||
'GET',
|
||||
'/share-networks/detail?description%7E=fake_description')
|
||||
|
||||
def test_share_network_list_filter_by_inexact_unicode_name(self):
|
||||
for separator in self.separators:
|
||||
self.run_command('share-network-list --name~' + separator +
|
||||
u'ффф')
|
||||
self.assert_called(
|
||||
'GET',
|
||||
'/share-networks/detail?name%7E=%D1%84%D1%84%D1%84')
|
||||
|
||||
def test_share_network_list_filter_by_inexact_unicode_description(self):
|
||||
for separator in self.separators:
|
||||
self.run_command('share-network-list --description~' + separator +
|
||||
u'ффф')
|
||||
self.assert_called(
|
||||
'GET',
|
||||
'/share-networks/detail?description%7E=%D1%84%D1%84%D1%84')
|
||||
|
||||
def test_share_network_security_service_add(self):
|
||||
self.run_command('share-network-security-service-add fake_share_nw '
|
||||
'fake_security_service')
|
||||
|
@@ -51,3 +51,21 @@ def get_function_name(func):
|
||||
return "%s.%s" % (func.__module__, func.__name__)
|
||||
else:
|
||||
return "%s.%s" % (func.__module__, func.__qualname__)
|
||||
|
||||
|
||||
def _encode(src):
|
||||
"""remove extra 'u' in PY2."""
|
||||
if six.PY2 and isinstance(src, unicode):
|
||||
return src.encode('utf-8')
|
||||
return src
|
||||
|
||||
|
||||
def unicode_key_value_to_string(src):
|
||||
"""Recursively converts dictionary keys to strings."""
|
||||
if isinstance(src, dict):
|
||||
return dict((_encode(k),
|
||||
_encode(unicode_key_value_to_string(v)))
|
||||
for k, v in src.items())
|
||||
if isinstance(src, list):
|
||||
return [unicode_key_value_to_string(l) for l in src]
|
||||
return _encode(src)
|
||||
|
@@ -13,15 +13,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
try:
|
||||
from urllib import urlencode # noqa
|
||||
except ImportError:
|
||||
from urllib.parse import urlencode # noqa
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from manilaclient import api_versions
|
||||
from manilaclient import base
|
||||
from manilaclient.common.apiclient import base as common_base
|
||||
from manilaclient import exceptions
|
||||
from manilaclient import utils
|
||||
|
||||
|
||||
RESOURCES_PATH = '/share-networks'
|
||||
@@ -227,13 +225,13 @@ class ShareNetworkManager(base.ManagerWithFind):
|
||||
if not search_opts:
|
||||
search_opts = {}
|
||||
|
||||
query_string = ""
|
||||
if search_opts:
|
||||
query_string = urlencode(
|
||||
sorted([(k, v) for (k, v) in list(search_opts.items()) if v]))
|
||||
if query_string:
|
||||
query_string = "?%s" % query_string
|
||||
else:
|
||||
query_string = ''
|
||||
search_opts = utils.unicode_key_value_to_string(search_opts)
|
||||
params = sorted(
|
||||
[(k, v) for (k, v) in list(search_opts.items()) if v])
|
||||
if params:
|
||||
query_string = "?%s" % parse.urlencode(params)
|
||||
|
||||
if detailed:
|
||||
path = RESOURCES_PATH + "/detail" + query_string
|
||||
|
@@ -14,15 +14,13 @@
|
||||
# under the License.
|
||||
"""Interface for shares extension."""
|
||||
|
||||
try:
|
||||
from urllib import urlencode # noqa
|
||||
except ImportError:
|
||||
from urllib.parse import urlencode # noqa
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from manilaclient import api_versions
|
||||
from manilaclient import base
|
||||
from manilaclient.common.apiclient import base as common_base
|
||||
from manilaclient.common import constants
|
||||
from manilaclient import utils
|
||||
|
||||
|
||||
class ShareSnapshot(common_base.Resource):
|
||||
@@ -153,13 +151,13 @@ class ShareSnapshotManager(base.ManagerWithFind):
|
||||
'sort_dir must be one of the following: %s.'
|
||||
% ', '.join(constants.SORT_DIR_VALUES))
|
||||
|
||||
query_string = ""
|
||||
if search_opts:
|
||||
query_string = urlencode(
|
||||
sorted([(k, v) for (k, v) in list(search_opts.items()) if v]))
|
||||
if query_string:
|
||||
query_string = "?%s" % (query_string,)
|
||||
else:
|
||||
query_string = ''
|
||||
search_opts = utils.unicode_key_value_to_string(search_opts)
|
||||
params = sorted(
|
||||
[(k, v) for (k, v) in list(search_opts.items()) if v])
|
||||
if params:
|
||||
query_string = "?%s" % parse.urlencode(params)
|
||||
|
||||
if detailed:
|
||||
path = "/snapshots/detail%s" % (query_string,)
|
||||
|
@@ -19,17 +19,15 @@ import ipaddress
|
||||
from oslo_utils import uuidutils
|
||||
import re
|
||||
import six
|
||||
from six.moves.urllib import parse
|
||||
import string
|
||||
try:
|
||||
from urllib import urlencode # noqa
|
||||
except ImportError:
|
||||
from urllib.parse import urlencode # noqa
|
||||
|
||||
from manilaclient import api_versions
|
||||
from manilaclient import base
|
||||
from manilaclient.common.apiclient import base as common_base
|
||||
from manilaclient.common import constants
|
||||
from manilaclient import exceptions
|
||||
from manilaclient import utils
|
||||
from manilaclient.v2 import share_instances
|
||||
|
||||
|
||||
@@ -399,13 +397,13 @@ class ShareManager(base.ManagerWithFind):
|
||||
else:
|
||||
search_opts['export_location_path'] = export_location
|
||||
|
||||
query_string = ""
|
||||
if search_opts:
|
||||
query_string = urlencode(
|
||||
sorted([(k, v) for (k, v) in list(search_opts.items()) if v]))
|
||||
if query_string:
|
||||
query_string = "?%s" % (query_string,)
|
||||
else:
|
||||
query_string = ''
|
||||
search_opts = utils.unicode_key_value_to_string(search_opts)
|
||||
params = sorted(
|
||||
[(k, v) for (k, v) in list(search_opts.items()) if v])
|
||||
if params:
|
||||
query_string = "?%s" % parse.urlencode(params)
|
||||
|
||||
if detailed:
|
||||
path = "/shares/detail%s" % (query_string,)
|
||||
|
@@ -1473,27 +1473,27 @@ def do_snapshot_access_list(cs, args):
|
||||
@cliutils.arg(
|
||||
'--name',
|
||||
metavar='<name>',
|
||||
type=str,
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results by name.')
|
||||
@cliutils.arg(
|
||||
'--description',
|
||||
metavar='<description>',
|
||||
type=str,
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results by description. '
|
||||
'Available only for microversion >= 2.36.')
|
||||
@cliutils.arg(
|
||||
'--name~',
|
||||
metavar='<name~>',
|
||||
type=str,
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results matching a share name pattern. '
|
||||
'Available only for microversion >= 2.36.')
|
||||
@cliutils.arg(
|
||||
'--description~',
|
||||
metavar='<description~>',
|
||||
type=str,
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results matching a share description pattern. '
|
||||
'Available only for microversion >= 2.36.')
|
||||
@@ -1886,12 +1886,13 @@ def do_share_instance_export_location_show(cs, args):
|
||||
@cliutils.arg(
|
||||
'--name',
|
||||
metavar='<name>',
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results by name.')
|
||||
@cliutils.arg(
|
||||
'--description',
|
||||
metavar='<description>',
|
||||
type=str,
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results by description. '
|
||||
'Available only for microversion >= 2.36.')
|
||||
@@ -1959,14 +1960,14 @@ def do_share_instance_export_location_show(cs, args):
|
||||
@cliutils.arg(
|
||||
'--name~',
|
||||
metavar='<name~>',
|
||||
type=str,
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results matching a share snapshot name pattern. '
|
||||
'Available only for microversion >= 2.36.')
|
||||
@cliutils.arg(
|
||||
'--description~',
|
||||
metavar='<description~>',
|
||||
type=str,
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results matching a share snapshot description pattern. '
|
||||
'Available only for microversion >= 2.36.')
|
||||
@@ -2740,12 +2741,13 @@ def do_share_network_list(cs, args):
|
||||
@cliutils.arg(
|
||||
'--name',
|
||||
metavar='<name>',
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results by name.')
|
||||
@cliutils.arg(
|
||||
'--description',
|
||||
metavar='<description>',
|
||||
type=str,
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results by description. '
|
||||
'Available only for microversion >= 2.36.')
|
||||
@@ -2837,14 +2839,14 @@ def do_share_network_list(cs, args):
|
||||
@cliutils.arg(
|
||||
'--name~',
|
||||
metavar='<name~>',
|
||||
type=str,
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results matching a share network name pattern. '
|
||||
'Available only for microversion >= 2.36.')
|
||||
@cliutils.arg(
|
||||
'--description~',
|
||||
metavar='<description~>',
|
||||
type=str,
|
||||
type=six.text_type,
|
||||
default=None,
|
||||
help='Filter results matching a share network description pattern. '
|
||||
'Available only for microversion >= 2.36.')
|
||||
|
Reference in New Issue
Block a user