Add possibility to create lots of shares in parallel for tempest
Test suite located in module contrib/tempest/tempest/api/share/admin/test_multi_backend.py creates 2 shares without waiting for "available" status and then waits for status. In this case "retry" functionality is not used. Add method 'create_shares' to base class that does parallel share creation with recreation as it is done with single request. And use this method in mentioned test module. Change-Id: I1314c9494a547314cd9bb9b03323470d58956fac Closes-Bug: #1406770
This commit is contained in:
parent
0dd28ab401
commit
a52d1b49a6
@ -36,6 +36,7 @@ class ShareMultiBackendTest(base.BaseSharesAdminTest):
|
|||||||
"Skipping.")
|
"Skipping.")
|
||||||
cls.vts = []
|
cls.vts = []
|
||||||
cls.shares = []
|
cls.shares = []
|
||||||
|
share_data_list = []
|
||||||
|
|
||||||
# Create volume types
|
# Create volume types
|
||||||
for i in [0, 1]:
|
for i in [0, 1]:
|
||||||
@ -46,16 +47,10 @@ class ShareMultiBackendTest(base.BaseSharesAdminTest):
|
|||||||
__, vt = cls.create_volume_type(name=vt_name,
|
__, vt = cls.create_volume_type(name=vt_name,
|
||||||
extra_specs=extra_specs)
|
extra_specs=extra_specs)
|
||||||
cls.vts.append(vt)
|
cls.vts.append(vt)
|
||||||
|
share_data_list.append({"kwargs": {"volume_type_id": vt["id"]}})
|
||||||
|
|
||||||
# Create shares
|
# Create shares using precreated volume types
|
||||||
for vt in cls.vts:
|
cls.shares = cls.create_shares(share_data_list)
|
||||||
__, share = cls.create_share(volume_type_id=vt["id"],
|
|
||||||
wait_for_active=False)
|
|
||||||
cls.shares.append(share)
|
|
||||||
|
|
||||||
# Wait for active statuses of shares
|
|
||||||
for share in cls.shares:
|
|
||||||
cls.shares_client.wait_for_share_status(share["id"], "available")
|
|
||||||
|
|
||||||
@test.attr(type=["gate", "smoke", ])
|
@test.attr(type=["gate", "smoke", ])
|
||||||
def test_share_backend_name_reporting(self):
|
def test_share_backend_name_reporting(self):
|
||||||
|
@ -13,6 +13,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.
|
||||||
|
|
||||||
|
import copy
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
from tempest import clients_share as clients
|
from tempest import clients_share as clients
|
||||||
@ -227,15 +228,15 @@ class BaseSharesTest(test.BaseTestCase):
|
|||||||
return share_network_id
|
return share_network_id
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_share(cls, share_protocol=None, size=1, name=None,
|
def _create_share(cls, share_protocol=None, size=1, name=None,
|
||||||
snapshot_id=None, description=None, metadata={},
|
snapshot_id=None, description=None, metadata=None,
|
||||||
share_network_id=None, volume_type_id=None,
|
share_network_id=None, volume_type_id=None,
|
||||||
client=None, cleanup_in_class=True,
|
client=None, cleanup_in_class=True):
|
||||||
wait_for_active=True):
|
|
||||||
client = client or cls.shares_client
|
client = client or cls.shares_client
|
||||||
description = description or "Tempest's share"
|
description = description or "Tempest's share"
|
||||||
share_network_id = share_network_id or client.share_network_id or None
|
share_network_id = share_network_id or client.share_network_id or None
|
||||||
kw = {
|
metadata = metadata or {}
|
||||||
|
kwargs = {
|
||||||
'share_protocol': share_protocol,
|
'share_protocol': share_protocol,
|
||||||
'size': size,
|
'size': size,
|
||||||
'name': name,
|
'name': name,
|
||||||
@ -245,33 +246,83 @@ class BaseSharesTest(test.BaseTestCase):
|
|||||||
'share_network_id': share_network_id,
|
'share_network_id': share_network_id,
|
||||||
'volume_type_id': volume_type_id,
|
'volume_type_id': volume_type_id,
|
||||||
}
|
}
|
||||||
|
resp, share = client.create_share(**kwargs)
|
||||||
|
resource = {"type": "share", "id": share["id"], "client": client}
|
||||||
|
cleanup_list = (cls.class_resources if cleanup_in_class else
|
||||||
|
cls.method_resources)
|
||||||
|
cleanup_list.insert(0, resource)
|
||||||
|
return resp, share
|
||||||
|
|
||||||
def _create_share(**kwargs):
|
@classmethod
|
||||||
resp, share = client.create_share(**kwargs)
|
def create_share(cls, *args, **kwargs):
|
||||||
resource = {"type": "share", "id": share["id"], "client": client}
|
"""Create one share and wait for available state. Retry if allowed."""
|
||||||
cleanup_list = (cls.class_resources if cleanup_in_class else
|
result = cls.create_shares(
|
||||||
cls.method_resources)
|
[{"args": args, "kwargs": kwargs}], with_resp=True)
|
||||||
cleanup_list.insert(0, resource)
|
return result[0]
|
||||||
return resp, share
|
|
||||||
|
|
||||||
resp, share = _create_share(**kw)
|
@classmethod
|
||||||
if wait_for_active:
|
def create_shares(cls, share_data_list, with_resp=False):
|
||||||
cnt = 0
|
"""Creates several shares in parallel with retries.
|
||||||
while True:
|
|
||||||
if cnt > 0:
|
Use this method when you want to create more than one share at same
|
||||||
resp, share = _create_share(**kw)
|
time. Especially if config option 'share.share_creation_retry_number'
|
||||||
|
has value more than zero (0).
|
||||||
|
All shares will be expected to have 'available' status with or without
|
||||||
|
recreation else error will be raised.
|
||||||
|
|
||||||
|
:param share_data_list: list -- list of dictionaries with 'args' and
|
||||||
|
'kwargs' for '_create_share' method of this base class.
|
||||||
|
example of data:
|
||||||
|
share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
|
||||||
|
:param with_resp: boolean -- whether to return list of
|
||||||
|
tuples (resp, share) or just list of shares.
|
||||||
|
:returns: list -- list of shares created using provided data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = [copy.deepcopy(d) for d in share_data_list]
|
||||||
|
for d in data:
|
||||||
|
if not isinstance(d, dict):
|
||||||
|
raise exceptions.TempestException(
|
||||||
|
"Expected 'dict', got '%s'" % type(d))
|
||||||
|
if "args" not in d:
|
||||||
|
d["args"] = []
|
||||||
|
if "kwargs" not in d:
|
||||||
|
d["kwargs"] = {}
|
||||||
|
if len(d) > 2:
|
||||||
|
raise exceptions.TempestException(
|
||||||
|
"Expected only 'args' and 'kwargs' keys. "
|
||||||
|
"Provided %s" % list(d))
|
||||||
|
d["kwargs"]["client"] = d["kwargs"].get(
|
||||||
|
"client", cls.shares_client)
|
||||||
|
d["resp"], d["share"] = cls._create_share(
|
||||||
|
*d["args"], **d["kwargs"])
|
||||||
|
d["cnt"] = 0
|
||||||
|
d["available"] = False
|
||||||
|
|
||||||
|
while not all(d["available"] for d in data):
|
||||||
|
for d in data:
|
||||||
|
if d["available"]:
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
client.wait_for_share_status(share["id"], "available")
|
d["kwargs"]["client"].wait_for_share_status(
|
||||||
|
d["share"]["id"], "available")
|
||||||
|
d["available"] = True
|
||||||
except (share_exceptions.ShareBuildErrorException,
|
except (share_exceptions.ShareBuildErrorException,
|
||||||
exceptions.TimeoutException) as e:
|
exceptions.TimeoutException) as e:
|
||||||
if CONF.share.share_creation_retry_number > cnt:
|
if CONF.share.share_creation_retry_number > d["cnt"]:
|
||||||
cnt += 1
|
d["cnt"] += 1
|
||||||
continue
|
msg = ("Share '%(id)s' failed to be built. "
|
||||||
|
"Trying create another." % d["share"]["id"])
|
||||||
|
LOG.error(msg)
|
||||||
|
LOG.error(e)
|
||||||
|
d["resp"], d["share"] = cls._create_share(
|
||||||
|
*d["args"], **d["kwargs"])
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
else:
|
|
||||||
break
|
if with_resp:
|
||||||
return resp, share
|
return [(d["resp"], d["share"]) for d in data]
|
||||||
|
return [d["share"] for d in data]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_snapshot_wait_for_active(cls, share_id, name=None,
|
def create_snapshot_wait_for_active(cls, share_id, name=None,
|
||||||
|
Loading…
Reference in New Issue
Block a user