Improve share list API filtering
This change adds possibility to filter shares in a lot of possible ways. All types of share listing were updated to support additional filtering by: - share network - snapshot - volume type - host - limit and offset a la pagination - project id (useful with '--all-tenants') - metadata (whether specific metadata keys and values are set or not) - extra-specs (whether any volume type exists and has specific extra-specs set up) And sorting by: - key of models.Share - direction (asc, desc) Implements blueprint improve-share-list-filtering Change-Id: Ifc53ab27bc710562cc510f30a2c847eda385d5bc
This commit is contained in:
parent
c5e48b592b
commit
8f0d0dc6c9
373
contrib/tempest/tempest/api/share/admin/test_shares_actions.py
Normal file
373
contrib/tempest/tempest/api/share/admin/test_shares_actions.py
Normal file
@ -0,0 +1,373 @@
|
||||
# Copyright 2014 Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from tempest.api.share import base
|
||||
from tempest.common.utils import data_utils
|
||||
from tempest import config_share as config
|
||||
from tempest import test
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class SharesActionsAdminTest(base.BaseSharesAdminTest):
|
||||
"""Covers share functionality, that doesn't related to share type."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(SharesActionsAdminTest, cls).setUpClass()
|
||||
|
||||
# create volume type for share filtering purposes
|
||||
cls.vt_name = data_utils.rand_name("tempest-vt-name")
|
||||
cls.extra_specs = {'storage_protocol': CONF.share.storage_protocol}
|
||||
__, cls.vt = cls.create_volume_type(
|
||||
name=cls.vt_name,
|
||||
cleanup_in_class=True,
|
||||
extra_specs=cls.extra_specs,
|
||||
)
|
||||
|
||||
# create share
|
||||
cls.share_name = data_utils.rand_name("tempest-share-name")
|
||||
cls.share_desc = data_utils.rand_name("tempest-share-description")
|
||||
cls.metadata = {
|
||||
'foo_key_share_1': 'foo_value_share_1',
|
||||
'bar_key_share_1': 'foo_value_share_1',
|
||||
}
|
||||
cls.share_size = 1
|
||||
__, cls.share = cls.create_share(
|
||||
name=cls.share_name,
|
||||
description=cls.share_desc,
|
||||
size=cls.share_size,
|
||||
metadata=cls.metadata,
|
||||
volume_type_id=cls.vt['id'],
|
||||
)
|
||||
|
||||
# create snapshot
|
||||
cls.snap_name = data_utils.rand_name("tempest-snapshot-name")
|
||||
cls.snap_desc = data_utils.rand_name("tempest-snapshot-description")
|
||||
__, cls.snap = cls.create_snapshot_wait_for_active(cls.share["id"],
|
||||
cls.snap_name,
|
||||
cls.snap_desc)
|
||||
|
||||
# create second share from snapshot for purposes of sorting and
|
||||
# snapshot filtering
|
||||
cls.share_name2 = data_utils.rand_name("tempest-share-name")
|
||||
cls.share_desc2 = data_utils.rand_name("tempest-share-description")
|
||||
cls.metadata2 = {
|
||||
'foo_key_share_2': 'foo_value_share_2',
|
||||
'bar_key_share_2': 'foo_value_share_2',
|
||||
}
|
||||
__, cls.share2 = cls.create_share(
|
||||
name=cls.share_name2,
|
||||
description=cls.share_desc2,
|
||||
size=cls.share_size,
|
||||
metadata=cls.metadata2,
|
||||
snapshot_id=cls.snap['id'],
|
||||
)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_get_share(self):
|
||||
|
||||
# get share
|
||||
resp, share = self.shares_client.get_share(self.share['id'])
|
||||
|
||||
# verify response
|
||||
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||
|
||||
# verify keys
|
||||
expected_keys = ["status", "description", "links", "availability_zone",
|
||||
"created_at", "export_location", "share_proto",
|
||||
"name", "snapshot_id", "id", "size"]
|
||||
actual_keys = share.keys()
|
||||
[self.assertIn(key, actual_keys) for key in expected_keys]
|
||||
|
||||
# verify values
|
||||
msg = "Expected name: '%s', actual name: '%s'" % (self.share_name,
|
||||
share["name"])
|
||||
self.assertEqual(self.share_name, str(share["name"]), msg)
|
||||
|
||||
msg = "Expected description: '%s', "\
|
||||
"actual description: '%s'" % (self.share_desc,
|
||||
share["description"])
|
||||
self.assertEqual(self.share_desc, str(share["description"]), msg)
|
||||
|
||||
msg = "Expected size: '%s', actual size: '%s'" % (self.share_size,
|
||||
share["size"])
|
||||
self.assertEqual(self.share_size, int(share["size"]), msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares(self):
|
||||
|
||||
# list shares
|
||||
resp, shares = self.shares_client.list_shares()
|
||||
|
||||
# verify response
|
||||
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||
|
||||
# verify keys
|
||||
keys = ["name", "id", "links"]
|
||||
[self.assertIn(key, sh.keys()) for sh in shares for key in keys]
|
||||
|
||||
# our share id in list and have no duplicates
|
||||
for share_id in [self.share["id"], self.share2["id"]]:
|
||||
gen = [sid["id"] for sid in shares if sid["id"] in share_id]
|
||||
msg = "expected id lists %s times in share list" % (len(gen))
|
||||
self.assertEqual(1, len(gen), msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail(self):
|
||||
|
||||
# list shares
|
||||
resp, shares = self.shares_client.list_shares_with_detail()
|
||||
|
||||
# verify response
|
||||
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||
|
||||
# verify keys
|
||||
keys = [
|
||||
"status", "description", "links", "availability_zone",
|
||||
"created_at", "export_location", "share_proto", "host",
|
||||
"name", "snapshot_id", "id", "size", "project_id",
|
||||
]
|
||||
[self.assertIn(key, sh.keys()) for sh in shares for key in keys]
|
||||
|
||||
# our shares in list and have no duplicates
|
||||
for share_id in [self.share["id"], self.share2["id"]]:
|
||||
gen = [sid["id"] for sid in shares if sid["id"] in share_id]
|
||||
msg = "expected id lists %s times in share list" % (len(gen))
|
||||
self.assertEqual(1, len(gen), msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_metadata(self):
|
||||
filters = {'metadata': self.metadata}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 0)
|
||||
for share in shares:
|
||||
self.assertDictContainsSubset(
|
||||
filters['metadata'], share['metadata'])
|
||||
self.assertFalse(self.share2['id'] in [s['id'] for s in shares])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_extra_specs(self):
|
||||
filters = {"extra_specs": self.extra_specs}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 0)
|
||||
shares_ids = [s["id"] for s in shares]
|
||||
self.assertTrue(self.share["id"] in shares_ids)
|
||||
self.assertFalse(self.share2["id"] in shares_ids)
|
||||
for share in shares:
|
||||
__, vts = self.shares_client.list_volume_types()
|
||||
# find its name or id, get id
|
||||
vt_id = None
|
||||
for vt in vts:
|
||||
if share["volume_type"] in [vt["id"], vt["name"]]:
|
||||
vt_id = vt["id"]
|
||||
break
|
||||
if vt_id is None:
|
||||
raise ValueError(
|
||||
"Share '%(s_id)s' listed with extra_specs filter has "
|
||||
"nonexistent volume type '%(vt)s'." % {
|
||||
"s_id": share["id"], "vt": share["volume_type"]}
|
||||
)
|
||||
__, extra_specs = self.shares_client.list_volume_types_extra_specs(
|
||||
vt_id)
|
||||
self.assertDictContainsSubset(filters["extra_specs"], extra_specs)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_volume_type_id(self):
|
||||
filters = {'volume_type_id': self.vt['id']}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 0)
|
||||
for share in shares:
|
||||
__, vts = self.shares_client.list_volume_types()
|
||||
# find its name or id, get id
|
||||
vt_id = None
|
||||
for vt in vts:
|
||||
if share["volume_type"] in [vt["id"], vt["name"]]:
|
||||
vt_id = vt["id"]
|
||||
break
|
||||
if vt_id is None:
|
||||
raise ValueError(
|
||||
"Share '%(s_id)s' listed with volume_type_id filter has "
|
||||
"nonexistent volume type '%(vt)s'." % {
|
||||
"s_id": share["id"], "vt": share["volume_type"]}
|
||||
)
|
||||
self.assertEqual(
|
||||
filters['volume_type_id'], vt_id)
|
||||
self.assertFalse(self.share2['id'] in [s['id'] for s in shares])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_host(self):
|
||||
__, base_share = self.shares_client.get_share(self.share['id'])
|
||||
filters = {'host': base_share['host']}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 0)
|
||||
for share in shares:
|
||||
self.assertEqual(filters['host'], share['host'])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_share_network_id(self):
|
||||
__, base_share = self.shares_client.get_share(self.share['id'])
|
||||
filters = {'share_network_id': base_share['share_network_id']}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 1)
|
||||
for share in shares:
|
||||
self.assertEqual(
|
||||
filters['share_network_id'], share['share_network_id'])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_snapshot_id(self):
|
||||
filters = {'snapshot_id': self.snap['id']}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 0)
|
||||
for share in shares:
|
||||
self.assertEqual(filters['snapshot_id'], share['snapshot_id'])
|
||||
self.assertFalse(self.share['id'] in [s['id'] for s in shares])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_with_asc_sorting(self):
|
||||
filters = {'sort_key': 'created_at', 'sort_dir': 'asc'}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 0)
|
||||
sorted_list = [share['created_at'] for share in shares]
|
||||
self.assertEqual(sorted_list, sorted(sorted_list))
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_existed_name(self):
|
||||
# list shares by name, at least one share is expected
|
||||
params = {"name": self.share_name}
|
||||
resp, shares = self.shares_client.list_shares_with_detail(params)
|
||||
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||
self.assertEqual(shares[0]["name"], self.share_name)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_fake_name(self):
|
||||
# list shares by fake name, no shares are expected
|
||||
params = {"name": data_utils.rand_name("fake-nonexistent-name")}
|
||||
resp, shares = self.shares_client.list_shares_with_detail(params)
|
||||
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||
self.assertEqual(0, len(shares))
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_active_status(self):
|
||||
# list shares by active status, at least one share is expected
|
||||
params = {"status": "available"}
|
||||
resp, shares = self.shares_client.list_shares_with_detail(params)
|
||||
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||
self.assertTrue(len(shares) > 0)
|
||||
for share in shares:
|
||||
self.assertEqual(share["status"], params["status"])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_fake_status(self):
|
||||
# list shares by fake status, no shares are expected
|
||||
params = {"status": 'fake'}
|
||||
resp, shares = self.shares_client.list_shares_with_detail(params)
|
||||
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||
self.assertEqual(0, len(shares))
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_get_snapshot(self):
|
||||
|
||||
# get snapshot
|
||||
resp, get = self.shares_client.get_snapshot(self.snap["id"])
|
||||
|
||||
# verify data
|
||||
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||
|
||||
# verify keys
|
||||
expected_keys = ["status", "links", "share_id", "name",
|
||||
"export_location", "share_proto", "created_at",
|
||||
"description", "id", "share_size"]
|
||||
actual_keys = get.keys()
|
||||
[self.assertIn(key, actual_keys) for key in expected_keys]
|
||||
|
||||
# verify data
|
||||
msg = "Expected name: '%s', actual name: '%s'" % (self.snap_name,
|
||||
get["name"])
|
||||
self.assertEqual(self.snap_name, get["name"], msg)
|
||||
|
||||
msg = "Expected description: '%s', "\
|
||||
"actual description: '%s'" % (self.snap_desc, get["description"])
|
||||
self.assertEqual(self.snap_desc, get["description"], msg)
|
||||
|
||||
msg = "Expected share_id: '%s', "\
|
||||
"actual share_id: '%s'" % (self.share["id"], get["share_id"])
|
||||
self.assertEqual(self.share["id"], get["share_id"], msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_snapshots(self):
|
||||
|
||||
# list share snapshots
|
||||
resp, snaps = self.shares_client.list_snapshots()
|
||||
|
||||
# verify response
|
||||
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||
|
||||
# verify keys
|
||||
keys = ["id", "name", "links"]
|
||||
[self.assertIn(key, sn.keys()) for sn in snaps for key in keys]
|
||||
|
||||
# our share id in list and have no duplicates
|
||||
gen = [sid["id"] for sid in snaps if sid["id"] in self.snap["id"]]
|
||||
msg = "expected id lists %s times in share list" % (len(gen))
|
||||
self.assertEqual(1, len(gen), msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_snapshots_with_detail(self):
|
||||
|
||||
# list share snapshots
|
||||
resp, snaps = self.shares_client.list_snapshots_with_detail()
|
||||
|
||||
# verify response
|
||||
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||
|
||||
# verify keys
|
||||
keys = ["status", "links", "share_id", "name",
|
||||
"export_location", "share_proto", "created_at",
|
||||
"description", "id", "share_size"]
|
||||
[self.assertIn(key, sn.keys()) for sn in snaps for key in keys]
|
||||
|
||||
# our share id in list and have no duplicates
|
||||
gen = [sid["id"] for sid in snaps if sid["id"] in self.snap["id"]]
|
||||
msg = "expected id lists %s times in share list" % (len(gen))
|
||||
self.assertEqual(1, len(gen), msg)
|
@ -1,4 +1,4 @@
|
||||
# Copyright 2014 mirantis Inc.
|
||||
# Copyright 2014 Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -21,25 +21,49 @@ from tempest import test
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class SharesTest(base.BaseSharesTest):
|
||||
class SharesActionsTest(base.BaseSharesTest):
|
||||
"""Covers share functionality, that doesn't related to share type."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(SharesTest, cls).setUpClass()
|
||||
super(SharesActionsTest, cls).setUpClass()
|
||||
|
||||
# create share
|
||||
cls.share_name = data_utils.rand_name("tempest-share-name")
|
||||
cls.share_desc = data_utils.rand_name("tempest-share-description")
|
||||
cls.metadata = {
|
||||
'foo_key_share_1': 'foo_value_share_1',
|
||||
'bar_key_share_1': 'foo_value_share_1',
|
||||
}
|
||||
cls.share_size = 1
|
||||
__, cls.share = cls.create_share(name=cls.share_name,
|
||||
description=cls.share_desc,
|
||||
size=cls.share_size)
|
||||
__, cls.share = cls.create_share(
|
||||
name=cls.share_name,
|
||||
description=cls.share_desc,
|
||||
size=cls.share_size,
|
||||
metadata=cls.metadata,
|
||||
)
|
||||
|
||||
# create snapshot
|
||||
cls.snap_name = data_utils.rand_name("tempest-snapshot-name")
|
||||
cls.snap_desc = data_utils.rand_name("tempest-snapshot-description")
|
||||
__, cls.snap = cls.create_snapshot_wait_for_active(cls.share["id"],
|
||||
cls.snap_name,
|
||||
cls.snap_desc)
|
||||
__, cls.snap = cls.create_snapshot_wait_for_active(
|
||||
cls.share["id"], cls.snap_name, cls.snap_desc)
|
||||
|
||||
# create second share from snapshot for purposes of sorting and
|
||||
# snapshot filtering
|
||||
cls.share_name2 = data_utils.rand_name("tempest-share-name")
|
||||
cls.share_desc2 = data_utils.rand_name("tempest-share-description")
|
||||
cls.metadata2 = {
|
||||
'foo_key_share_2': 'foo_value_share_2',
|
||||
'bar_key_share_2': 'foo_value_share_2',
|
||||
}
|
||||
__, cls.share2 = cls.create_share(
|
||||
name=cls.share_name2,
|
||||
description=cls.share_desc2,
|
||||
size=cls.share_size,
|
||||
metadata=cls.metadata2,
|
||||
snapshot_id=cls.snap['id'],
|
||||
)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_get_share(self):
|
||||
@ -85,9 +109,10 @@ class SharesTest(base.BaseSharesTest):
|
||||
[self.assertIn(key, sh.keys()) for sh in shares for key in keys]
|
||||
|
||||
# our share id in list and have no duplicates
|
||||
gen = [sid["id"] for sid in shares if sid["id"] in self.share["id"]]
|
||||
msg = "expected id lists %s times in share list" % (len(gen))
|
||||
self.assertEqual(len(gen), 1, msg)
|
||||
for share_id in [self.share["id"], self.share2["id"]]:
|
||||
gen = [sid["id"] for sid in shares if sid["id"] in share_id]
|
||||
msg = "expected id lists %s times in share list" % (len(gen))
|
||||
self.assertEqual(1, len(gen), msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail(self):
|
||||
@ -106,10 +131,77 @@ class SharesTest(base.BaseSharesTest):
|
||||
]
|
||||
[self.assertIn(key, sh.keys()) for sh in shares for key in keys]
|
||||
|
||||
# our share id in list and have no duplicates
|
||||
gen = [sid["id"] for sid in shares if sid["id"] in self.share["id"]]
|
||||
msg = "expected id lists %s times in share list" % (len(gen))
|
||||
self.assertEqual(len(gen), 1, msg)
|
||||
# our shares in list and have no duplicates
|
||||
for share_id in [self.share["id"], self.share2["id"]]:
|
||||
gen = [sid["id"] for sid in shares if sid["id"] in share_id]
|
||||
msg = "expected id lists %s times in share list" % (len(gen))
|
||||
self.assertEqual(1, len(gen), msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_metadata(self):
|
||||
filters = {'metadata': self.metadata}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 0)
|
||||
for share in shares:
|
||||
self.assertDictContainsSubset(
|
||||
filters['metadata'], share['metadata'])
|
||||
self.assertFalse(self.share2['id'] in [s['id'] for s in shares])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_host(self):
|
||||
__, base_share = self.shares_client.get_share(self.share['id'])
|
||||
filters = {'host': base_share['host']}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 0)
|
||||
for share in shares:
|
||||
self.assertEqual(filters['host'], share['host'])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_share_network_id(self):
|
||||
__, base_share = self.shares_client.get_share(self.share['id'])
|
||||
filters = {'share_network_id': base_share['share_network_id']}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 1)
|
||||
for share in shares:
|
||||
self.assertEqual(
|
||||
filters['share_network_id'], share['share_network_id'])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_snapshot_id(self):
|
||||
filters = {'snapshot_id': self.snap['id']}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 0)
|
||||
for share in shares:
|
||||
self.assertEqual(filters['snapshot_id'], share['snapshot_id'])
|
||||
self.assertFalse(self.share['id'] in [s['id'] for s in shares])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_with_asc_sorting(self):
|
||||
filters = {'sort_key': 'created_at', 'sort_dir': 'asc'}
|
||||
|
||||
# list shares
|
||||
__, shares = self.shares_client.list_shares_with_detail(params=filters)
|
||||
|
||||
# verify response
|
||||
self.assertTrue(len(shares) > 0)
|
||||
sorted_list = [share['created_at'] for share in shares]
|
||||
self.assertEqual(sorted_list, sorted(sorted_list))
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_shares_with_detail_filter_by_existed_name(self):
|
||||
|
@ -80,17 +80,16 @@ class SharesClient(rest_client.RestClient):
|
||||
def delete_share(self, share_id):
|
||||
return self.delete("shares/%s" % share_id)
|
||||
|
||||
def list_shares(self):
|
||||
resp, body = self.get("shares")
|
||||
def list_shares(self, detailed=False, params=None):
|
||||
"""Get list of shares w/o filters."""
|
||||
uri = 'shares/detail' if detailed else 'shares'
|
||||
uri += '?%s' % urllib.urlencode(params) if params else ''
|
||||
resp, body = self.get(uri)
|
||||
return resp, self._parse_resp(body)
|
||||
|
||||
def list_shares_with_detail(self, params=None):
|
||||
"""List the details of all shares."""
|
||||
uri = 'shares/detail'
|
||||
if params:
|
||||
uri += '?%s' % urllib.urlencode(params)
|
||||
resp, body = self.get(uri)
|
||||
return resp, self._parse_resp(body)
|
||||
"""Get detailed list of shares w/o filters."""
|
||||
return self.list_shares(detailed=True, params=params)
|
||||
|
||||
def get_share(self, share_id):
|
||||
resp, body = self.get("shares/%s" % share_id)
|
||||
|
@ -15,6 +15,8 @@
|
||||
|
||||
"""The shares api."""
|
||||
|
||||
import ast
|
||||
|
||||
import six
|
||||
import webob
|
||||
from webob import exc
|
||||
@ -110,15 +112,33 @@ class ShareController(wsgi.Controller):
|
||||
search_opts = {}
|
||||
search_opts.update(req.GET)
|
||||
|
||||
# NOTE(rushiagr): v2 API allows name instead of display_name
|
||||
# Remove keys that are not related to share attrs
|
||||
search_opts.pop('limit', None)
|
||||
search_opts.pop('offset', None)
|
||||
sort_key = search_opts.pop('sort_key', 'created_at')
|
||||
sort_dir = search_opts.pop('sort_dir', 'desc')
|
||||
|
||||
# Deserialize dicts
|
||||
if 'metadata' in search_opts:
|
||||
search_opts['metadata'] = ast.literal_eval(search_opts['metadata'])
|
||||
if 'extra_specs' in search_opts:
|
||||
search_opts['extra_specs'] = ast.literal_eval(
|
||||
search_opts['extra_specs'])
|
||||
|
||||
# NOTE(vponomaryov): Manila stores in DB key 'display_name', but
|
||||
# allows to use both keys 'name' and 'display_name'. It is leftover
|
||||
# from Cinder v1 and v2 APIs.
|
||||
if 'name' in search_opts:
|
||||
search_opts['display_name'] = search_opts['name']
|
||||
del search_opts['name']
|
||||
search_opts['display_name'] = search_opts.pop('name')
|
||||
if sort_key == 'name':
|
||||
sort_key = 'display_name'
|
||||
|
||||
common.remove_invalid_options(
|
||||
context, search_opts, self._get_share_search_options())
|
||||
|
||||
shares = self.share_api.get_all(context, search_opts=search_opts)
|
||||
shares = self.share_api.get_all(
|
||||
context, search_opts=search_opts, sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
limited_list = common.limited(shares, req)
|
||||
|
||||
@ -132,7 +152,13 @@ class ShareController(wsgi.Controller):
|
||||
"""Return share search options allowed by non-admin."""
|
||||
# NOTE(vponomaryov): share_server_id depends on policy, allow search
|
||||
# by it for non-admins in case policy changed.
|
||||
return ('display_name', 'status', 'share_server_id', )
|
||||
# Also allow search by extra_specs in case policy
|
||||
# for it allows non-admin access.
|
||||
return (
|
||||
'display_name', 'status', 'share_server_id', 'volume_type_id',
|
||||
'snapshot_id', 'host', 'share_network_id',
|
||||
'metadata', 'extra_specs', 'sort_key', 'sort_dir',
|
||||
)
|
||||
|
||||
@wsgi.serializers(xml=ShareTemplate)
|
||||
def update(self, req, id, body):
|
||||
|
@ -294,24 +294,37 @@ def share_get(context, share_id):
|
||||
return IMPL.share_get(context, share_id)
|
||||
|
||||
|
||||
def share_get_all(context):
|
||||
def share_get_all(context, filters=None, sort_key=None, sort_dir=None):
|
||||
"""Get all shares."""
|
||||
return IMPL.share_get_all(context)
|
||||
return IMPL.share_get_all(
|
||||
context, filters=filters, sort_key=sort_key, sort_dir=sort_dir,
|
||||
)
|
||||
|
||||
|
||||
def share_get_all_by_host(context, host):
|
||||
def share_get_all_by_host(context, host, filters=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
"""Returns all shares with given host."""
|
||||
return IMPL.share_get_all_by_host(context, host)
|
||||
return IMPL.share_get_all_by_host(
|
||||
context, host, filters=filters, sort_key=sort_key, sort_dir=sort_dir,
|
||||
)
|
||||
|
||||
|
||||
def share_get_all_by_project(context, project_id):
|
||||
def share_get_all_by_project(context, project_id, filters=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
"""Returns all shares with given project ID."""
|
||||
return IMPL.share_get_all_by_project(context, project_id)
|
||||
return IMPL.share_get_all_by_project(
|
||||
context, project_id, filters=filters, sort_key=sort_key,
|
||||
sort_dir=sort_dir,
|
||||
)
|
||||
|
||||
|
||||
def share_get_all_by_share_server(context, share_server_id):
|
||||
"""Returns all shares with given share server."""
|
||||
return IMPL.share_get_all_by_share_server(context, share_server_id)
|
||||
def share_get_all_by_share_server(context, share_server_id, filters=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Returns all shares with given share server ID."""
|
||||
return IMPL.share_get_all_by_share_server(
|
||||
context, share_server_id, filters=filters, sort_key=sort_key,
|
||||
sort_dir=sort_dir,
|
||||
)
|
||||
|
||||
|
||||
def share_delete(context, share_id):
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# Copyright (c) 2014 Mirantis, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -1164,28 +1165,109 @@ def share_get(context, share_id, session=None):
|
||||
return result
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def share_get_all(context):
|
||||
return _share_get_query(context).all()
|
||||
@require_context
|
||||
def _share_get_all_with_filters(context, project_id=None, share_server_id=None,
|
||||
host=None, filters=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Returns sorted list of shares that satisfies filters.
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def share_get_all_by_host(context, host):
|
||||
:param context: context to query under
|
||||
:param project_id: project id that owns shares
|
||||
:param share_server_id: share server that hosts shares
|
||||
:param host: host name where shares [and share servers] are located
|
||||
:param filters: dict of filters to specify share selection
|
||||
:param sort_key: key of models.Share to be used for sorting
|
||||
:param sort_dir: desired direction of sorting, can be 'asc' and 'desc'
|
||||
:returns: list -- models.Share
|
||||
:raises: exception.InvalidInput
|
||||
"""
|
||||
if not sort_key:
|
||||
sort_key = 'created_at'
|
||||
if not sort_dir:
|
||||
sort_dir = 'desc'
|
||||
query = _share_get_query(context)
|
||||
return query.filter_by(host=host).all()
|
||||
if project_id:
|
||||
query = query.filter_by(project_id=project_id)
|
||||
if share_server_id:
|
||||
query = query.filter_by(share_server_id=share_server_id)
|
||||
if host:
|
||||
query = query.filter_by(host=host)
|
||||
|
||||
# Apply filters
|
||||
if not filters:
|
||||
filters = {}
|
||||
if 'metadata' in filters:
|
||||
for k, v in filters['metadata'].items():
|
||||
query = query.filter(
|
||||
or_(models.Share.share_metadata.any( # pylint: disable=E1101
|
||||
key=k, value=v)))
|
||||
if 'extra_specs' in filters:
|
||||
query = query.join(
|
||||
models.VolumeTypeExtraSpecs,
|
||||
models.VolumeTypeExtraSpecs.volume_type_id ==
|
||||
models.Share.volume_type_id)
|
||||
for k, v in filters['extra_specs'].items():
|
||||
query = query.filter(or_(models.VolumeTypeExtraSpecs.key == k,
|
||||
models.VolumeTypeExtraSpecs.value == v))
|
||||
|
||||
# Apply sorting
|
||||
try:
|
||||
attr = getattr(models.Share, sort_key)
|
||||
except AttributeError:
|
||||
msg = _("Wrong sorting key provided - '%s'.") % sort_key
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
if sort_dir.lower() == 'desc':
|
||||
query = query.order_by(attr.desc())
|
||||
elif sort_dir.lower() == 'asc':
|
||||
query = query.order_by(attr.asc())
|
||||
else:
|
||||
msg = _("Wrong sorting data provided: sort key is '%(sort_key)s' "
|
||||
"and sort direction is '%(sort_dir)s'.") % {
|
||||
"sort_key": sort_key, "sort_dir": sort_dir}
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
# Returns list of shares that satisfy filters.
|
||||
query = query.all()
|
||||
return query
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def share_get_all(context, filters=None, sort_key=None, sort_dir=None):
|
||||
query = _share_get_all_with_filters(
|
||||
context, filters=filters, sort_key=sort_key, sort_dir=sort_dir)
|
||||
return query
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def share_get_all_by_host(context, host, filters=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
query = _share_get_all_with_filters(
|
||||
context, host=host, filters=filters,
|
||||
sort_key=sort_key, sort_dir=sort_dir,
|
||||
)
|
||||
return query
|
||||
|
||||
|
||||
@require_context
|
||||
def share_get_all_by_project(context, project_id):
|
||||
def share_get_all_by_project(context, project_id, filters=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Returns list of shares with given project ID."""
|
||||
return _share_get_query(context).filter_by(project_id=project_id).all()
|
||||
query = _share_get_all_with_filters(
|
||||
context, project_id=project_id, filters=filters,
|
||||
sort_key=sort_key, sort_dir=sort_dir,
|
||||
)
|
||||
return query
|
||||
|
||||
|
||||
@require_context
|
||||
def share_get_all_by_share_server(context, share_server_id):
|
||||
def share_get_all_by_share_server(context, share_server_id, filters=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Returns list of shares with given share server."""
|
||||
return _share_get_query(context).filter_by(
|
||||
share_server_id=share_server_id).all()
|
||||
query = _share_get_all_with_filters(
|
||||
context, share_server_id=share_server_id, filters=filters,
|
||||
sort_key=sort_key, sort_dir=sort_dir,
|
||||
)
|
||||
return query
|
||||
|
||||
|
||||
@require_context
|
||||
|
@ -21,6 +21,7 @@ Handles all requests relating to shares.
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
|
||||
from manila.api import extensions
|
||||
from manila.db import base
|
||||
from manila import exception
|
||||
from manila.openstack.common import excutils
|
||||
@ -337,24 +338,60 @@ class API(base.Base):
|
||||
policy.check_policy(context, 'share', 'get', rv)
|
||||
return rv
|
||||
|
||||
def get_all(self, context, search_opts=None):
|
||||
def get_all(self, context, search_opts=None, sort_key='created_at',
|
||||
sort_dir='desc'):
|
||||
policy.check_policy(context, 'share', 'get_all')
|
||||
|
||||
if search_opts is None:
|
||||
search_opts = {}
|
||||
|
||||
LOG.debug("Searching for shares by: %s", six.text_type(search_opts))
|
||||
|
||||
# Prepare filters
|
||||
filters = {}
|
||||
if 'metadata' in search_opts:
|
||||
filters['metadata'] = search_opts.pop('metadata')
|
||||
if not isinstance(filters['metadata'], dict):
|
||||
msg = _("Wrong metadata filter provided: "
|
||||
"%s.") % six.text_type(filters['metadata'])
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
if 'extra_specs' in search_opts:
|
||||
# Verify policy for extra-specs access
|
||||
extensions.extension_authorizer(
|
||||
'share', 'types_extra_specs')(context)
|
||||
filters['extra_specs'] = search_opts.pop('extra_specs')
|
||||
if not isinstance(filters['extra_specs'], dict):
|
||||
msg = _("Wrong extra specs filter provided: "
|
||||
"%s.") % six.text_type(filters['extra_specs'])
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
if not (isinstance(sort_key, six.string_types) and sort_key):
|
||||
msg = _("Wrong sort_key filter provided: "
|
||||
"'%s'.") % six.text_type(sort_key)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
if not (isinstance(sort_dir, six.string_types) and sort_dir):
|
||||
msg = _("Wrong sort_dir filter provided: "
|
||||
"'%s'.") % six.text_type(sort_dir)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
# Get filtered list of shares
|
||||
if 'share_server_id' in search_opts:
|
||||
# NOTE(vponomaryov): this is project_id independent
|
||||
policy.check_policy(context, 'share', 'list_by_share_server_id')
|
||||
shares = self.db.share_get_all_by_share_server(
|
||||
context, search_opts.pop('share_server_id'))
|
||||
context, search_opts.pop('share_server_id'), filters=filters,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
elif (context.is_admin and 'all_tenants' in search_opts):
|
||||
shares = self.db.share_get_all(context)
|
||||
shares = self.db.share_get_all(
|
||||
context, filters=filters, sort_key=sort_key, sort_dir=sort_dir)
|
||||
else:
|
||||
shares = self.db.share_get_all_by_project(context,
|
||||
context.project_id)
|
||||
shares = self.db.share_get_all_by_project(
|
||||
context, project_id=context.project_id, filters=filters,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
# NOTE(vponomaryov): we do not need 'all_tenants' opt anymore
|
||||
search_opts.pop('all_tenants', None)
|
||||
|
||||
if search_opts:
|
||||
LOG.debug("Searching for shares by: %s", str(search_opts))
|
||||
results = []
|
||||
for s in shares:
|
||||
# values in search_opts can be only strings
|
||||
|
@ -86,7 +86,8 @@ def stub_snapshot_update(self, context, *args, **param):
|
||||
return share
|
||||
|
||||
|
||||
def stub_share_get_all_by_project(self, context, search_opts=None):
|
||||
def stub_share_get_all_by_project(self, context, sort_key=None, sort_dir=None,
|
||||
search_opts={}):
|
||||
return [stub_share_get(self, context, '1')]
|
||||
|
||||
|
||||
|
@ -322,50 +322,67 @@ class ShareApiTest(test.TestCase):
|
||||
req,
|
||||
1)
|
||||
|
||||
def test_share_list_summary_with_search_opts_by_non_admin(self):
|
||||
def _share_list_summary_with_search_opts(self, use_admin_context):
|
||||
search_opts = {
|
||||
'name': 'fake_name',
|
||||
'status': 'available',
|
||||
'share_server_id': 'fake_share_server_id',
|
||||
'volume_type_id': 'fake_volume_type_id',
|
||||
'snapshot_id': 'fake_snapshot_id',
|
||||
'host': 'fake_host',
|
||||
'share_network_id': 'fake_share_network_id',
|
||||
'metadata': '%7B%27k1%27%3A+%27v1%27%7D', # serialized k1=v1
|
||||
'extra_specs': '%7B%27k2%27%3A+%27v2%27%7D', # serialized k2=v2
|
||||
'sort_key': 'fake_sort_key',
|
||||
'sort_dir': 'fake_sort_dir',
|
||||
'limit': '1',
|
||||
'offset': '1',
|
||||
}
|
||||
# fake_key should be filtered for non-admin
|
||||
fake_key = 'fake_value'
|
||||
name = 'fake_name'
|
||||
status = 'available'
|
||||
share_server_id = 'fake_share_server_id'
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares?fake_key=%s&name=%s&share_server_id=%s&'
|
||||
'status=%s' % (fake_key, name, share_server_id, status),
|
||||
use_admin_context=False,
|
||||
)
|
||||
self.stubs.Set(share_api.API, 'get_all', mock.Mock(return_value=[]))
|
||||
self.controller.index(req)
|
||||
url = '/shares?fake_key=fake_value'
|
||||
for k, v in search_opts.items():
|
||||
url = url + '&' + k + '=' + v
|
||||
req = fakes.HTTPRequest.blank(url, use_admin_context=use_admin_context)
|
||||
|
||||
shares = [
|
||||
{'id': 'id1', 'display_name': 'n1'},
|
||||
{'id': 'id2', 'display_name': 'n2'},
|
||||
{'id': 'id3', 'display_name': 'n3'},
|
||||
]
|
||||
self.stubs.Set(share_api.API, 'get_all',
|
||||
mock.Mock(return_value=shares))
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
search_opts_expected = {
|
||||
'display_name': search_opts['name'],
|
||||
'status': search_opts['status'],
|
||||
'share_server_id': search_opts['share_server_id'],
|
||||
'volume_type_id': search_opts['volume_type_id'],
|
||||
'snapshot_id': search_opts['snapshot_id'],
|
||||
'host': search_opts['host'],
|
||||
'share_network_id': search_opts['share_network_id'],
|
||||
'metadata': {'k1': 'v1'},
|
||||
'extra_specs': {'k2': 'v2'},
|
||||
}
|
||||
if use_admin_context:
|
||||
search_opts_expected.update({'fake_key': 'fake_value'})
|
||||
share_api.API.get_all.assert_called_once_with(
|
||||
req.environ['manila.context'],
|
||||
search_opts={
|
||||
'display_name': name,
|
||||
'share_server_id': share_server_id,
|
||||
'status': status,
|
||||
},
|
||||
sort_key=search_opts['sort_key'],
|
||||
sort_dir=search_opts['sort_dir'],
|
||||
search_opts=search_opts_expected,
|
||||
)
|
||||
self.assertEqual(1, len(result['shares']))
|
||||
self.assertEqual(shares[1]['id'], result['shares'][0]['id'])
|
||||
self.assertEqual(
|
||||
shares[1]['display_name'], result['shares'][0]['name'])
|
||||
|
||||
def test_share_list_summary_with_search_opts_by_non_admin(self):
|
||||
self._share_list_summary_with_search_opts(use_admin_context=False)
|
||||
|
||||
def test_share_list_summary_with_search_opts_by_admin(self):
|
||||
# none of search_opts should be filtered for admin
|
||||
fake_key = 'fake_value'
|
||||
name = 'fake_name'
|
||||
status = 'available'
|
||||
share_server_id = 'fake_share_server_id'
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares?fake_key=%s&name=%s&share_server_id=%s&'
|
||||
'status=%s' % (fake_key, name, share_server_id, status),
|
||||
use_admin_context=True,
|
||||
)
|
||||
self.stubs.Set(share_api.API, 'get_all', mock.Mock(return_value=[]))
|
||||
self.controller.index(req)
|
||||
share_api.API.get_all.assert_called_once_with(
|
||||
req.environ['manila.context'],
|
||||
search_opts={
|
||||
'display_name': name,
|
||||
'fake_key': fake_key,
|
||||
'share_server_id': share_server_id,
|
||||
'status': status,
|
||||
},
|
||||
)
|
||||
self._share_list_summary_with_search_opts(use_admin_context=True)
|
||||
|
||||
def test_share_list_summary(self):
|
||||
self.stubs.Set(share_api.API, 'get_all',
|
||||
@ -392,50 +409,89 @@ class ShareApiTest(test.TestCase):
|
||||
}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_share_list_detail_with_search_opts_by_non_admin(self):
|
||||
def _share_list_detail_with_search_opts(self, use_admin_context):
|
||||
search_opts = {
|
||||
'name': 'fake_name',
|
||||
'status': 'available',
|
||||
'share_server_id': 'fake_share_server_id',
|
||||
'volume_type_id': 'fake_volume_type_id',
|
||||
'snapshot_id': 'fake_snapshot_id',
|
||||
'host': 'fake_host',
|
||||
'share_network_id': 'fake_share_network_id',
|
||||
'metadata': '%7B%27k1%27%3A+%27v1%27%7D', # serialized k1=v1
|
||||
'extra_specs': '%7B%27k2%27%3A+%27v2%27%7D', # serialized k2=v2
|
||||
'sort_key': 'fake_sort_key',
|
||||
'sort_dir': 'fake_sort_dir',
|
||||
'limit': '1',
|
||||
'offset': '1',
|
||||
}
|
||||
# fake_key should be filtered for non-admin
|
||||
fake_key = 'fake_value'
|
||||
name = 'fake_name'
|
||||
status = 'available'
|
||||
share_server_id = 'fake_share_server_id'
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares?fake_key=%s&name=%s&share_server_id=%s&'
|
||||
'status=%s' % (fake_key, name, share_server_id, status),
|
||||
use_admin_context=False,
|
||||
)
|
||||
self.stubs.Set(share_api.API, 'get_all', mock.Mock(return_value=[]))
|
||||
self.controller.detail(req)
|
||||
url = '/shares/detail?fake_key=fake_value'
|
||||
for k, v in search_opts.items():
|
||||
url = url + '&' + k + '=' + v
|
||||
req = fakes.HTTPRequest.blank(url, use_admin_context=use_admin_context)
|
||||
|
||||
shares = [
|
||||
{'id': 'id1', 'display_name': 'n1'},
|
||||
{
|
||||
'id': 'id2',
|
||||
'display_name': 'n2',
|
||||
'status': 'available',
|
||||
'snapshot_id': 'fake_snapshot_id',
|
||||
'volume_type_id': 'fake_volume_type_id',
|
||||
'snapshot_id': 'fake_snapshot_id',
|
||||
'host': 'fake_host',
|
||||
'share_network_id': 'fake_share_network_id',
|
||||
},
|
||||
{'id': 'id3', 'display_name': 'n3'},
|
||||
]
|
||||
self.stubs.Set(share_api.API, 'get_all',
|
||||
mock.Mock(return_value=shares))
|
||||
|
||||
result = self.controller.detail(req)
|
||||
|
||||
search_opts_expected = {
|
||||
'display_name': search_opts['name'],
|
||||
'status': search_opts['status'],
|
||||
'share_server_id': search_opts['share_server_id'],
|
||||
'volume_type_id': search_opts['volume_type_id'],
|
||||
'snapshot_id': search_opts['snapshot_id'],
|
||||
'host': search_opts['host'],
|
||||
'share_network_id': search_opts['share_network_id'],
|
||||
'metadata': {'k1': 'v1'},
|
||||
'extra_specs': {'k2': 'v2'},
|
||||
}
|
||||
if use_admin_context:
|
||||
search_opts_expected.update({'fake_key': 'fake_value'})
|
||||
share_api.API.get_all.assert_called_once_with(
|
||||
req.environ['manila.context'],
|
||||
search_opts={
|
||||
'display_name': name,
|
||||
'share_server_id': share_server_id,
|
||||
'status': status,
|
||||
},
|
||||
sort_key=search_opts['sort_key'],
|
||||
sort_dir=search_opts['sort_dir'],
|
||||
search_opts=search_opts_expected,
|
||||
)
|
||||
self.assertEqual(1, len(result['shares']))
|
||||
self.assertEqual(shares[1]['id'], result['shares'][0]['id'])
|
||||
self.assertEqual(
|
||||
shares[1]['display_name'], result['shares'][0]['name'])
|
||||
self.assertEqual(
|
||||
shares[1]['snapshot_id'], result['shares'][0]['snapshot_id'])
|
||||
self.assertEqual(
|
||||
shares[1]['status'], result['shares'][0]['status'])
|
||||
self.assertEqual(
|
||||
shares[1]['volume_type_id'], result['shares'][0]['volume_type'])
|
||||
self.assertEqual(
|
||||
shares[1]['snapshot_id'], result['shares'][0]['snapshot_id'])
|
||||
self.assertEqual(
|
||||
shares[1]['host'], result['shares'][0]['host'])
|
||||
self.assertEqual(
|
||||
shares[1]['share_network_id'],
|
||||
result['shares'][0]['share_network_id'])
|
||||
|
||||
def test_share_list_detail_with_search_opts_by_non_admin(self):
|
||||
self._share_list_detail_with_search_opts(use_admin_context=False)
|
||||
|
||||
def test_share_list_detail_with_search_opts_by_admin(self):
|
||||
# none of search_opts should be filtered for admin
|
||||
fake_key = 'fake_value'
|
||||
name = 'fake_name'
|
||||
status = 'available'
|
||||
share_server_id = 'fake_share_server_id'
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares?fake_key=%s&name=%s&share_server_id=%s&'
|
||||
'status=%s' % (fake_key, name, share_server_id, status),
|
||||
use_admin_context=True,
|
||||
)
|
||||
self.stubs.Set(share_api.API, 'get_all', mock.Mock(return_value=[]))
|
||||
self.controller.detail(req)
|
||||
share_api.API.get_all.assert_called_once_with(
|
||||
req.environ['manila.context'],
|
||||
search_opts={
|
||||
'display_name': name,
|
||||
'fake_key': fake_key,
|
||||
'share_server_id': share_server_id,
|
||||
'status': status,
|
||||
},
|
||||
)
|
||||
self._share_list_detail_with_search_opts(use_admin_context=True)
|
||||
|
||||
def test_share_list_detail(self):
|
||||
self.stubs.Set(share_api.API, 'get_all',
|
||||
|
@ -158,7 +158,9 @@ class ShareAPITestCase(test.TestCase):
|
||||
share_api.policy.check_policy.assert_called_once_with(
|
||||
ctx, 'share', 'get_all')
|
||||
db_driver.share_get_all_by_project.assert_called_once_with(
|
||||
ctx, 'fake_pid_1')
|
||||
ctx, sort_dir='desc', sort_key='created_at',
|
||||
project_id='fake_pid_1', filters={},
|
||||
)
|
||||
self.assertEqual(shares, _FAKE_LIST_OF_ALL_SHARES[0])
|
||||
|
||||
def test_get_all_admin_filter_by_all_tenants(self):
|
||||
@ -168,7 +170,8 @@ class ShareAPITestCase(test.TestCase):
|
||||
shares = self.api.get_all(ctx, {'all_tenants': 1})
|
||||
share_api.policy.check_policy.assert_called_once_with(
|
||||
ctx, 'share', 'get_all')
|
||||
db_driver.share_get_all.assert_called_once_with(ctx)
|
||||
db_driver.share_get_all.assert_called_once_with(
|
||||
ctx, sort_dir='desc', sort_key='created_at', filters={})
|
||||
self.assertEqual(shares, _FAKE_LIST_OF_ALL_SHARES)
|
||||
|
||||
def test_get_all_non_admin_filter_by_share_server(self):
|
||||
@ -206,7 +209,9 @@ class ShareAPITestCase(test.TestCase):
|
||||
mock.call(ctx, 'share', 'list_by_share_server_id'),
|
||||
])
|
||||
db_driver.share_get_all_by_share_server.assert_called_once_with(
|
||||
ctx, 'fake_server_3')
|
||||
ctx, 'fake_server_3', sort_dir='desc', sort_key='created_at',
|
||||
filters={},
|
||||
)
|
||||
db_driver.share_get_all_by_project.assert_has_calls([])
|
||||
db_driver.share_get_all.assert_has_calls([])
|
||||
self.assertEqual(shares, _FAKE_LIST_OF_ALL_SHARES[2:])
|
||||
@ -220,7 +225,9 @@ class ShareAPITestCase(test.TestCase):
|
||||
mock.call(ctx, 'share', 'get_all'),
|
||||
])
|
||||
db_driver.share_get_all_by_project.assert_called_once_with(
|
||||
ctx, 'fake_pid_2')
|
||||
ctx, sort_dir='desc', sort_key='created_at',
|
||||
project_id='fake_pid_2', filters={},
|
||||
)
|
||||
self.assertEqual(shares, _FAKE_LIST_OF_ALL_SHARES[1::2])
|
||||
|
||||
def test_get_all_admin_filter_by_name_and_all_tenants(self):
|
||||
@ -231,7 +238,8 @@ class ShareAPITestCase(test.TestCase):
|
||||
share_api.policy.check_policy.assert_has_calls([
|
||||
mock.call(ctx, 'share', 'get_all'),
|
||||
])
|
||||
db_driver.share_get_all.assert_called_once_with(ctx)
|
||||
db_driver.share_get_all.assert_called_once_with(
|
||||
ctx, sort_dir='desc', sort_key='created_at', filters={})
|
||||
self.assertEqual(shares, _FAKE_LIST_OF_ALL_SHARES[::2])
|
||||
|
||||
def test_get_all_admin_filter_by_status(self):
|
||||
@ -243,7 +251,9 @@ class ShareAPITestCase(test.TestCase):
|
||||
mock.call(ctx, 'share', 'get_all'),
|
||||
])
|
||||
db_driver.share_get_all_by_project.assert_called_once_with(
|
||||
ctx, 'fake_pid_2')
|
||||
ctx, sort_dir='desc', sort_key='created_at',
|
||||
project_id='fake_pid_2', filters={}
|
||||
)
|
||||
self.assertEqual(shares, _FAKE_LIST_OF_ALL_SHARES[2::4])
|
||||
|
||||
def test_get_all_admin_filter_by_status_and_all_tenants(self):
|
||||
@ -254,7 +264,8 @@ class ShareAPITestCase(test.TestCase):
|
||||
share_api.policy.check_policy.assert_has_calls([
|
||||
mock.call(ctx, 'share', 'get_all'),
|
||||
])
|
||||
db_driver.share_get_all.assert_called_once_with(ctx)
|
||||
db_driver.share_get_all.assert_called_once_with(
|
||||
ctx, sort_dir='desc', sort_key='created_at', filters={})
|
||||
self.assertEqual(shares, _FAKE_LIST_OF_ALL_SHARES[1::2])
|
||||
|
||||
def test_get_all_non_admin_filter_by_all_tenants(self):
|
||||
@ -267,7 +278,9 @@ class ShareAPITestCase(test.TestCase):
|
||||
mock.call(ctx, 'share', 'get_all'),
|
||||
])
|
||||
db_driver.share_get_all_by_project.assert_called_once_with(
|
||||
ctx, 'fake_pid_2')
|
||||
ctx, sort_dir='desc', sort_key='created_at',
|
||||
project_id='fake_pid_2', filters={},
|
||||
)
|
||||
self.assertEqual(shares, _FAKE_LIST_OF_ALL_SHARES[1:])
|
||||
|
||||
def test_get_all_non_admin_with_name_and_status_filters(self):
|
||||
@ -279,7 +292,9 @@ class ShareAPITestCase(test.TestCase):
|
||||
mock.call(ctx, 'share', 'get_all'),
|
||||
])
|
||||
db_driver.share_get_all_by_project.assert_called_once_with(
|
||||
ctx, 'fake_pid_2')
|
||||
ctx, sort_dir='desc', sort_key='created_at',
|
||||
project_id='fake_pid_2', filters={},
|
||||
)
|
||||
|
||||
# two items expected, one filtered
|
||||
self.assertEqual(shares, _FAKE_LIST_OF_ALL_SHARES[1::2])
|
||||
@ -292,10 +307,86 @@ class ShareAPITestCase(test.TestCase):
|
||||
mock.call(ctx, 'share', 'get_all'),
|
||||
])
|
||||
db_driver.share_get_all_by_project.assert_has_calls([
|
||||
mock.call(ctx, 'fake_pid_2'),
|
||||
mock.call(ctx, 'fake_pid_2'),
|
||||
mock.call(ctx, sort_dir='desc', sort_key='created_at',
|
||||
project_id='fake_pid_2', filters={}),
|
||||
mock.call(ctx, sort_dir='desc', sort_key='created_at',
|
||||
project_id='fake_pid_2', filters={}),
|
||||
])
|
||||
|
||||
def test_get_all_with_sorting_valid(self):
|
||||
self.stubs.Set(db_driver, 'share_get_all_by_project',
|
||||
mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
|
||||
ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
|
||||
shares = self.api.get_all(ctx, sort_key='status', sort_dir='asc')
|
||||
share_api.policy.check_policy.assert_called_once_with(
|
||||
ctx, 'share', 'get_all')
|
||||
db_driver.share_get_all_by_project.assert_called_once_with(
|
||||
ctx, sort_dir='asc', sort_key='status',
|
||||
project_id='fake_pid_1', filters={},
|
||||
)
|
||||
self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[0], shares)
|
||||
|
||||
def test_get_all_sort_key_invalid(self):
|
||||
self.stubs.Set(db_driver, 'share_get_all_by_project',
|
||||
mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
|
||||
ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
|
||||
self.assertRaises(
|
||||
exception.InvalidInput,
|
||||
self.api.get_all,
|
||||
ctx,
|
||||
sort_key=1,
|
||||
)
|
||||
share_api.policy.check_policy.assert_called_once_with(
|
||||
ctx, 'share', 'get_all')
|
||||
|
||||
def test_get_all_sort_dir_invalid(self):
|
||||
self.stubs.Set(db_driver, 'share_get_all_by_project',
|
||||
mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
|
||||
ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
|
||||
self.assertRaises(
|
||||
exception.InvalidInput,
|
||||
self.api.get_all,
|
||||
ctx,
|
||||
sort_dir=1,
|
||||
)
|
||||
share_api.policy.check_policy.assert_called_once_with(
|
||||
ctx, 'share', 'get_all')
|
||||
|
||||
def _get_all_filter_metadata_or_extra_specs_valid(self, key):
|
||||
self.stubs.Set(db_driver, 'share_get_all_by_project',
|
||||
mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
|
||||
ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
|
||||
search_opts = {key: {'foo1': 'bar1', 'foo2': 'bar2'}}
|
||||
shares = self.api.get_all(ctx, search_opts=search_opts.copy())
|
||||
share_api.policy.check_policy.assert_called_once_with(
|
||||
ctx, 'share', 'get_all')
|
||||
db_driver.share_get_all_by_project.assert_called_once_with(
|
||||
ctx, sort_dir='desc', sort_key='created_at',
|
||||
project_id='fake_pid_1', filters=search_opts)
|
||||
self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[0], shares)
|
||||
|
||||
def test_get_all_filter_by_metadata(self):
|
||||
self._get_all_filter_metadata_or_extra_specs_valid(key='metadata')
|
||||
|
||||
def test_get_all_filter_by_extra_specs(self):
|
||||
self._get_all_filter_metadata_or_extra_specs_valid(key='extra_specs')
|
||||
|
||||
def _get_all_filter_metadata_or_extra_specs_invalid(self, key):
|
||||
self.stubs.Set(db_driver, 'share_get_all_by_project',
|
||||
mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
|
||||
ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
|
||||
search_opts = {key: "{'foo': 'bar'}"}
|
||||
self.assertRaises(exception.InvalidInput, self.api.get_all, ctx,
|
||||
search_opts=search_opts)
|
||||
share_api.policy.check_policy.assert_called_once_with(
|
||||
ctx, 'share', 'get_all')
|
||||
|
||||
def test_get_all_filter_by_invalid_metadata(self):
|
||||
self._get_all_filter_metadata_or_extra_specs_invalid(key='metadata')
|
||||
|
||||
def test_get_all_filter_by_invalid_extra_specs(self):
|
||||
self._get_all_filter_metadata_or_extra_specs_invalid(key='extra_specs')
|
||||
|
||||
def test_create(self):
|
||||
date = datetime.datetime(1, 1, 1, 1, 1, 1)
|
||||
self.mock_utcnow.return_value = date
|
||||
|
Loading…
Reference in New Issue
Block a user