[Manila] Add scenarios for Manila share metadata

List of changes:
- Added support for 'set' and 'delete' operations for share metadata.
- Added benchmark for setting and deleting share metadata.

Change-Id: I0ecb8c13218f23fd359045b05e0b596455b401b9
This commit is contained in:
Valeriy Ponomaryov 2016-11-04 20:47:57 +02:00
parent 02435f13f7
commit e63d7e7567
14 changed files with 779 additions and 3 deletions

View File

@ -58,3 +58,35 @@
failure_rate:
max: 0
{% endfor %}
ManilaShares.set_and_delete_metadata:
-
args:
sets: 1
set_size: 3
delete_size: 3
key_min_length: 1
key_max_length: 256
value_min_length: 1
value_max_length: 1024
runner:
type: "constant"
times: 10
concurrency: 10
context:
quotas:
manila:
shares: -1
gigabytes: -1
users:
tenants: 1
users_per_tenant: 1
manila_shares:
shares_per_tenant: 1
share_proto: "NFS"
size: 1
share_type: "dhss_false"
sla:
failure_rate:
max: 0

View File

@ -162,3 +162,37 @@
failure_rate:
max: 0
{% endfor %}
ManilaShares.set_and_delete_metadata:
-
args:
sets: 1
set_size: 3
delete_size: 3
key_min_length: 1
key_max_length: 256
value_min_length: 1
value_max_length: 1024
runner:
type: "constant"
times: 10
concurrency: 10
context:
quotas:
manila:
shares: -1
gigabytes: -1
share_networks: -1
users:
tenants: 1
users_per_tenant: 1
manila_share_networks:
use_share_networks: True
manila_shares:
shares_per_tenant: 1
share_proto: "NFS"
size: 1
share_type: "dhss_true"
sla:
failure_rate:
max: 0

View File

@ -13,5 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
SHARES_CONTEXT_NAME = "manila_shares"
SHARE_NETWORKS_CONTEXT_NAME = "manila_share_networks"
SECURITY_SERVICES_CONTEXT_NAME = "manila_security_services"

View File

@ -33,7 +33,7 @@ CONTEXT_NAME = consts.SHARE_NETWORKS_CONTEXT_NAME
@context.configure(name=CONTEXT_NAME, order=450)
class ShareNetworks(context.Context):
"""This context creates resources specific for Manila project."""
"""This context creates share networks for Manila project."""
CONFIG_SCHEMA = {
"type": "object",
"$schema": rally_consts.JSON_SCHEMA,

View File

@ -0,0 +1,107 @@
# Copyright 2016 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 oslo_config import cfg
from rally.common.i18n import _
from rally.common import logging
from rally.common import utils
from rally import consts as rally_consts
from rally.plugins.openstack.cleanup import manager as resource_manager
from rally.plugins.openstack.context.manila import consts
from rally.plugins.openstack.scenarios.manila import utils as manila_utils
from rally.task import context
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
CONTEXT_NAME = consts.SHARES_CONTEXT_NAME
@context.configure(name=CONTEXT_NAME, order=455)
class Shares(context.Context):
"""This context creates shares for Manila project."""
CONFIG_SCHEMA = {
"type": "object",
"$schema": rally_consts.JSON_SCHEMA,
"properties": {
"shares_per_tenant": {
"type": "integer",
"minimum": 1,
},
"size": {
"type": "integer",
"minimum": 1
},
"share_proto": {
"type": "string",
},
"share_type": {
"type": "string",
},
},
"additionalProperties": False
}
DEFAULT_CONFIG = {
"shares_per_tenant": 1,
"size": 1,
"share_proto": "NFS",
"share_type": None,
}
def _create_shares(self, manila_scenario, tenant_id, share_proto, size=1,
share_type=None):
tenant_ctxt = self.context["tenants"][tenant_id]
tenant_ctxt.setdefault("shares", [])
for i in range(self.config["shares_per_tenant"]):
kwargs = {"share_proto": share_proto, "size": size}
if share_type:
kwargs["share_type"] = share_type
share_networks = tenant_ctxt.get("manila_share_networks", {}).get(
"share_networks", [])
if share_networks:
kwargs["share_network"] = share_networks[
i % len(share_networks)]["id"]
share = manila_scenario._create_share(**kwargs)
tenant_ctxt["shares"].append(share.to_dict())
@logging.log_task_wrapper(
LOG.info, _("Enter context: `%s`") % CONTEXT_NAME)
def setup(self):
for user, tenant_id in (
utils.iterate_per_tenants(self.context.get("users", []))):
manila_scenario = manila_utils.ManilaScenario({
"task": self.task,
"user": user,
"config": {
"api_versions": self.context["config"].get(
"api_versions", [])}
})
self._create_shares(
manila_scenario,
tenant_id,
self.config["share_proto"],
self.config["size"],
self.config["share_type"],
)
@logging.log_task_wrapper(LOG.info, _("Exit context: `%s`") % CONTEXT_NAME)
def cleanup(self):
resource_manager.cleanup(
names=["manila.shares"],
users=self.context.get("users", []),
)

View File

@ -15,6 +15,7 @@
from rally.common import logging
from rally import consts
from rally.plugins.openstack.context.manila import consts as manila_consts
from rally.plugins.openstack import scenario
from rally.plugins.openstack.scenarios.manila import utils
from rally.task import validation
@ -228,3 +229,53 @@ class CreateAndListShare(utils.ManilaScenario):
self._create_share(share_proto=share_proto, size=size, **kwargs)
self.sleep_between(min_sleep, max_sleep)
self._list_shares(detailed=detailed)
@validation.number("sets", minval=1, integer_only=True)
@validation.number("set_size", minval=1, integer_only=True)
@validation.number("key_min_length", minval=1, maxval=256, integer_only=True)
@validation.number("key_max_length", minval=1, maxval=256, integer_only=True)
@validation.number(
"value_min_length", minval=1, maxval=1024, integer_only=True)
@validation.number(
"value_max_length", minval=1, maxval=1024, integer_only=True)
@validation.required_services(consts.Service.MANILA)
@validation.required_openstack(users=True)
@validation.required_contexts(manila_consts.SHARES_CONTEXT_NAME)
@scenario.configure(
context={"cleanup": ["manila"]},
name="ManilaShares.set_and_delete_metadata")
class SetAndDeleteMetadata(utils.ManilaScenario):
def run(self, sets=10, set_size=3, delete_size=3,
key_min_length=1, key_max_length=256,
value_min_length=1, value_max_length=1024):
"""Sets and deletes share metadata.
This requires a share to be created with the shares
context. Additionally, ``sets * set_size`` must be greater
than or equal to ``deletes * delete_size``.
:param sets: how many set_metadata operations to perform
:param set_size: number of metadata keys to set in each
set_metadata operation
:param delete_size: number of metadata keys to delete in each
delete_metadata operation
:param key_min_length: minimal size of metadata key to set
:param key_max_length: maximum size of metadata key to set
:param value_min_length: minimal size of metadata value to set
:param value_max_length: maximum size of metadata value to set
"""
shares = self.context.get("tenant", {}).get("shares", [])
share = shares[self.context["iteration"] % len(shares)]
keys = self._set_metadata(
share=share,
sets=sets,
set_size=set_size,
key_min_length=key_min_length,
key_max_length=key_max_length,
value_min_length=value_min_length,
value_max_length=value_max_length)
self._delete_metadata(share=share, keys=keys, delete_size=delete_size)

View File

@ -13,9 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import random
from oslo_config import cfg
from rally import exceptions
from rally.plugins.openstack.context.manila import consts
from rally.plugins.openstack import scenario
from rally.task import atomic
@ -244,3 +246,65 @@ class ManilaScenario(scenario.OpenStackScenario):
"manila").share_networks.add_security_service(
share_network, security_service)
return share_network
@atomic.action_timer("manila.set_metadata")
def _set_metadata(self, share, sets=1, set_size=1,
key_min_length=1, key_max_length=256,
value_min_length=1, value_max_length=1024):
"""Sets share metadata.
:param share: the share to set metadata on
:param sets: how many operations to perform
:param set_size: number of metadata keys to set in each operation
:param key_min_length: minimal size of metadata key to set
:param key_max_length: maximum size of metadata key to set
:param value_min_length: minimal size of metadata value to set
:param value_max_length: maximum size of metadata value to set
:returns: A list of keys that were set
:raises exceptions.InvalidArgumentsException: if invalid arguments
were provided.
"""
if not (key_min_length <= key_max_length and
value_min_length <= value_max_length):
raise exceptions.InvalidArgumentsException(
"Min length for keys and values of metadata can not be bigger "
"than maximum length.")
keys = []
for i in range(sets):
metadata = {}
for j in range(set_size):
if key_min_length == key_max_length:
key_length = key_min_length
else:
key_length = random.choice(
range(key_min_length, key_max_length))
if value_min_length == value_max_length:
value_length = value_min_length
else:
value_length = random.choice(
range(value_min_length, value_max_length))
key = self._generate_random_part(length=key_length)
keys.append(key)
metadata[key] = self._generate_random_part(length=value_length)
self.clients("manila").shares.set_metadata(share["id"], metadata)
return keys
@atomic.action_timer("manila.delete_metadata")
def _delete_metadata(self, share, keys, delete_size=3):
"""Deletes share metadata.
:param share: The share to delete metadata from.
:param delete_size: number of metadata keys to delete using one single
call.
:param keys: a list or tuple of keys to choose deletion candidates from
:raises exceptions.InvalidArgumentsException: if invalid arguments
were provided.
"""
if not (isinstance(keys, list) and keys):
raise exceptions.InvalidArgumentsException(
"Param 'keys' should be non-empty 'list'. keys = '%s'" % keys)
for i in range(0, len(keys), delete_size):
self.clients("manila").shares.delete_metadata(
share["id"], keys[i:i + delete_size])

View File

@ -0,0 +1,51 @@
{
"ManilaShares.set_and_delete_metadata": [
{
"args": {
"sets": 1,
"set_size": 3,
"delete_size": 3,
"key_min_length": 1,
"key_max_length": 256,
"value_min_length": 1,
"value_max_length": 1024
},
"runner": {
"type": "constant",
"times": 1,
"concurrency": 1
},
"context": {
"quotas": {
"manila": {
"shares": -1,
"gigabytes": -1,
"share_networks": -1
}
},
"users": {
"tenants": 1,
"users_per_tenant": 1,
"user_choice_method": "round_robin"
},
"network": {
"networks_per_tenant": 1,
"start_cidr": "99.0.0.0/24"
},
"manila_share_networks": {
"use_share_networks": true
},
"manila_shares": {
"shares_per_tenant": 1,
"share_proto": "NFS",
"size": 1
}
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@ -0,0 +1,37 @@
---
ManilaShares.set_and_delete_metadata:
-
args:
sets: 1
set_size: 3
delete_size: 3
key_min_length: 1
key_max_length: 256
value_min_length: 1
value_max_length: 1024
runner:
type: "constant"
times: 1
concurrency: 1
context:
quotas:
manila:
shares: -1
gigabytes: -1
share_networks: -1
users:
tenants: 1
users_per_tenant: 1
user_choice_method: "round_robin"
network:
networks_per_tenant: 1
start_cidr: "99.0.0.0/24"
manila_share_networks:
use_share_networks: True
manila_shares:
shares_per_tenant: 1
share_proto: "NFS"
size: 1
sla:
failure_rate:
max: 0

View File

@ -0,0 +1,44 @@
{
"ManilaShares.set_and_delete_metadata": [
{
"args": {
"sets": 1,
"set_size": 3,
"delete_size": 3,
"key_min_length": 1,
"key_max_length": 256,
"value_min_length": 1,
"value_max_length": 1024
},
"runner": {
"type": "constant",
"times": 1,
"concurrency": 1
},
"context": {
"quotas": {
"manila": {
"shares": -1,
"gigabytes": -1,
"share_networks": -1
}
},
"users": {
"tenants": 1,
"users_per_tenant": 1,
"user_choice_method": "round_robin"
},
"manila_shares": {
"shares_per_tenant": 1,
"share_proto": "NFS",
"size": 1
}
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@ -0,0 +1,32 @@
---
ManilaShares.set_and_delete_metadata:
-
args:
sets: 1
set_size: 3
delete_size: 3
key_min_length: 1
key_max_length: 256
value_min_length: 1
value_max_length: 1024
runner:
type: "constant"
times: 1
concurrency: 1
context:
quotas:
manila:
shares: -1
gigabytes: -1
share_networks: -1
users:
tenants: 1
users_per_tenant: 1
user_choice_method: "round_robin"
manila_shares:
shares_per_tenant: 1
share_proto: "NFS"
size: 1
sla:
failure_rate:
max: 0

View File

@ -0,0 +1,202 @@
# Copyright 2016 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.
import copy
import ddt
import mock
import six
from rally import consts as rally_consts
from rally.plugins.openstack.context.manila import consts
from rally.plugins.openstack.context.manila import manila_shares
from tests.unit import test
MANILA_UTILS_PATH = (
"rally.plugins.openstack.scenarios.manila.utils.ManilaScenario.")
class Fake(object):
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def __getitem__(self, item):
return getattr(self, item)
def to_dict(self):
return self.__dict__
@ddt.ddt
class SharesTestCase(test.TestCase):
TENANTS_AMOUNT = 3
USERS_PER_TENANT = 4
SHARES_PER_TENANT = 7
SHARE_NETWORKS = [{"id": "sn_%s_id" % d} for d in range(3)]
def _get_context(self, use_share_networks=False, shares_per_tenant=None,
share_size=1, share_proto="fake_proto", share_type=None):
tenants = {}
for t_id in range(self.TENANTS_AMOUNT):
tenants[six.text_type(t_id)] = {"name": six.text_type(t_id)}
users = []
for t_id in sorted(list(tenants.keys())):
for i in range(self.USERS_PER_TENANT):
users.append(
{"id": i, "tenant_id": t_id, "credential": "fake"})
context = {
"config": {
"users": {
"tenants": self.TENANTS_AMOUNT,
"users_per_tenant": self.USERS_PER_TENANT,
"user_choice_method": "round_robin",
},
consts.SHARE_NETWORKS_CONTEXT_NAME: {
"use_share_networks": use_share_networks,
"share_networks": self.SHARE_NETWORKS,
},
consts.SHARES_CONTEXT_NAME: {
"shares_per_tenant": (
shares_per_tenant or self.SHARES_PER_TENANT),
"size": share_size,
"share_proto": share_proto,
"share_type": share_type,
},
},
"admin": {
"credential": mock.MagicMock(),
},
"task": mock.MagicMock(),
"users": users,
"tenants": tenants,
}
if use_share_networks:
for t in context["tenants"].keys():
context["tenants"][t][consts.SHARE_NETWORKS_CONTEXT_NAME] = {
"share_networks": self.SHARE_NETWORKS,
}
return context
def test_init(self):
ctxt = {
"task": mock.MagicMock(),
"config": {
consts.SHARES_CONTEXT_NAME: {"foo": "bar"},
"fake": {"fake_key": "fake_value"},
},
}
inst = manila_shares.Shares(ctxt)
self.assertEqual(
{"foo": "bar", "shares_per_tenant": 1, "size": 1,
"share_proto": "NFS", "share_type": None},
inst.config)
self.assertIn(
rally_consts.JSON_SCHEMA, inst.CONFIG_SCHEMA.get("$schema"))
self.assertFalse(inst.CONFIG_SCHEMA.get("additionalProperties"))
self.assertEqual("object", inst.CONFIG_SCHEMA.get("type"))
props = inst.CONFIG_SCHEMA.get("properties", {})
self.assertEqual(
{"minimum": 1, "type": "integer"}, props.get("shares_per_tenant"))
self.assertEqual({"minimum": 1, "type": "integer"}, props.get("size"))
self.assertEqual({"type": "string"}, props.get("share_proto"))
self.assertEqual({"type": "string"}, props.get("share_type"))
self.assertEqual(455, inst.get_order())
self.assertEqual(consts.SHARES_CONTEXT_NAME, inst.get_name())
@mock.patch(MANILA_UTILS_PATH + "_create_share")
@ddt.data(True, False)
def test_setup(
self,
use_share_networks,
mock_manila_scenario__create_share):
share_type = "fake_share_type"
ctxt = self._get_context(
use_share_networks=use_share_networks, share_type=share_type)
inst = manila_shares.Shares(ctxt)
shares = [
Fake(id="fake_share_id_%d" % s_id)
for s_id in range(self.TENANTS_AMOUNT * self.SHARES_PER_TENANT)
]
mock_manila_scenario__create_share.side_effect = shares
expected_ctxt = copy.deepcopy(ctxt)
inst.setup()
self.assertEqual(
self.TENANTS_AMOUNT * self.SHARES_PER_TENANT,
mock_manila_scenario__create_share.call_count)
for d in range(self.TENANTS_AMOUNT):
self.assertEqual(
[
s.to_dict() for s in shares[
(d * self.SHARES_PER_TENANT):(
d * self.SHARES_PER_TENANT + self.SHARES_PER_TENANT
)
]
],
inst.context.get("tenants", {}).get("%s" % d, {}).get("shares")
)
self.assertEqual(expected_ctxt["task"], inst.context.get("task"))
self.assertEqual(expected_ctxt["config"], inst.context.get("config"))
self.assertEqual(expected_ctxt["users"], inst.context.get("users"))
if use_share_networks:
mock_calls = [
mock.call(
share_proto=ctxt["config"][consts.SHARES_CONTEXT_NAME][
"share_proto"],
size=ctxt["config"][consts.SHARES_CONTEXT_NAME]["size"],
share_type=ctxt["config"][consts.SHARES_CONTEXT_NAME][
"share_type"],
share_network=self.SHARE_NETWORKS[
int(t_id) % len(self.SHARE_NETWORKS)]["id"]
) for t_id in expected_ctxt["tenants"].keys()
]
else:
mock_calls = [
mock.call(
share_proto=ctxt["config"][consts.SHARES_CONTEXT_NAME][
"share_proto"],
size=ctxt["config"][consts.SHARES_CONTEXT_NAME]["size"],
share_type=ctxt["config"][consts.SHARES_CONTEXT_NAME][
"share_type"],
) for t_id in expected_ctxt["tenants"].keys()
]
mock_manila_scenario__create_share.assert_has_calls(
mock_calls, any_order=True)
@mock.patch(MANILA_UTILS_PATH + "_create_share")
@mock.patch("rally.plugins.openstack.cleanup.manager.cleanup")
def test_cleanup(
self,
mock_cleanup_manager_cleanup,
mock_manila_scenario__create_share):
ctxt = self._get_context()
inst = manila_shares.Shares(ctxt)
shares = [
Fake(id="fake_share_id_%d" % s_id)
for s_id in range(self.TENANTS_AMOUNT * self.SHARES_PER_TENANT)
]
mock_manila_scenario__create_share.side_effect = shares
inst.setup()
inst.cleanup()
mock_cleanup_manager_cleanup.assert_called_once_with(
names=["manila.shares"],
users=inst.context.get("users", []),
)

View File

@ -209,3 +209,37 @@ class ManilaSharesTestCase(test.ScenarioTestCase):
scenario._create_share.assert_called_once_with(**params)
scenario.sleep_between.assert_called_once_with(3, 4)
scenario._list_shares.assert_called_once_with(detailed=detailed)
@ddt.data(
({}, 0, 0),
({}, 1, 1),
({}, 2, 2),
({}, 3, 0),
({"sets": 5, "set_size": 8, "delete_size": 10}, 1, 1),
)
@ddt.unpack
def test_set_and_delete_metadata(self, params, iteration, share_number):
scenario = shares.SetAndDeleteMetadata()
share_list = [{"id": "fake_share_%s_id" % d} for d in range(3)]
scenario.context = {"tenant": {"shares": share_list}}
scenario.context["iteration"] = iteration
scenario._set_metadata = mock.MagicMock()
scenario._delete_metadata = mock.MagicMock()
expected_set_params = {
"share": share_list[share_number],
"sets": params.get("sets", 10),
"set_size": params.get("set_size", 3),
"key_min_length": params.get("key_min_length", 1),
"key_max_length": params.get("key_max_length", 256),
"value_min_length": params.get("value_min_length", 1),
"value_max_length": params.get("value_max_length", 1024),
}
scenario.run(**params)
scenario._set_metadata.assert_called_once_with(**expected_set_params)
scenario._delete_metadata.assert_called_once_with(
share=share_list[share_number],
keys=scenario._set_metadata.return_value,
delete_size=params.get("delete_size", 3),
)

View File

@ -16,6 +16,7 @@
import ddt
import mock
from rally import exceptions
from rally.plugins.openstack.context.manila import consts
from rally.plugins.openstack.scenarios.manila import utils
from tests.unit import test
@ -41,12 +42,14 @@ class ManilaScenarioTestCase(test.ScenarioTestCase):
},
"iteration": 0,
}
self.scenario.generate_random_name = mock.Mock()
fake_random_name = "fake_random_name_value"
self.scenario.generate_random_name = mock.Mock(
return_value=fake_random_name)
self.scenario._create_share("nfs")
self.clients("manila").shares.create.assert_called_once_with(
"nfs", 1, name=self.scenario.generate_random_name.return_value,
"nfs", 1, name=fake_random_name,
share_network=self.scenario.context["tenant"][
consts.SHARE_NETWORKS_CONTEXT_NAME]["share_networks"][0]["id"])
@ -213,3 +216,87 @@ class ManilaScenarioTestCase(test.ScenarioTestCase):
self.clients(
"manila").share_networks.add_security_service.assert_has_calls([
mock.call(fake_sn, fake_ss)])
@ddt.data(
{"key_min_length": 5, "key_max_length": 4},
{"value_min_length": 5, "value_max_length": 4},
)
def test__set_metadata_wrong_params(self, params):
self.assertRaises(
exceptions.InvalidArgumentsException,
self.scenario._set_metadata,
{"id": "fake_share_id"}, **params)
@ddt.data(
{},
{"sets": 0, "set_size": 1},
{"sets": 1, "set_size": 1},
{"sets": 5, "set_size": 7},
{"sets": 5, "set_size": 2},
{"key_min_length": 1, "key_max_length": 1},
{"key_min_length": 1, "key_max_length": 2},
{"key_min_length": 256, "key_max_length": 256},
{"value_min_length": 1, "value_max_length": 1},
{"value_min_length": 1, "value_max_length": 2},
{"value_min_length": 1024, "value_max_length": 1024},
)
def test__set_metadata(self, params):
share = {"id": "fake_share_id"}
sets = params.get("sets", 1)
set_size = params.get("set_size", 1)
gen_name_calls = sets * set_size * 2
data = range(gen_name_calls)
generator_data = iter(data)
def fake_random_name(prefix="fake", length="fake"):
return next(generator_data)
scenario = self.scenario
scenario.clients = mock.MagicMock()
scenario._generate_random_part = mock.MagicMock(
side_effect=fake_random_name)
keys = scenario._set_metadata(share, **params)
self.assertEqual(
gen_name_calls,
scenario._generate_random_part.call_count)
self.assertEqual(
params.get("sets", 1),
scenario.clients.return_value.shares.set_metadata.call_count)
scenario.clients.return_value.shares.set_metadata.assert_has_calls([
mock.call(
share["id"],
dict([(j, j + 1) for j in data[
i * set_size * 2: (i + 1) * set_size * 2: 2]])
) for i in range(sets)
])
self.assertEqual([i for i in range(0, gen_name_calls, 2)], keys)
@ddt.data(None, [], {"fake_set"}, {"fake_key": "fake_value"})
def test__delete_metadata_wrong_params(self, keys):
self.assertRaises(
exceptions.InvalidArgumentsException,
self.scenario._delete_metadata,
"fake_share", keys=keys,
)
@ddt.data(
{"keys": [i for i in range(30)]},
{"keys": list(range(7)), "delete_size": 2},
{"keys": list(range(7)), "delete_size": 3},
{"keys": list(range(7)), "delete_size": 4},
)
def test__delete_metadata(self, params):
share = {"id": "fake_share_id"}
delete_size = params.get("delete_size", 3)
keys = params.get("keys", [])
scenario = self.scenario
scenario.clients = mock.MagicMock()
scenario._delete_metadata(share, **params)
scenario.clients.return_value.shares.delete_metadata.assert_has_calls([
mock.call(share["id"], keys[i:i + delete_size])
for i in range(0, len(keys), delete_size)
])