[senlin]Add context support for Senlin profile
This patch adds context support for Senlin profile. The following changes were made: - Adding Senlin profile context; - Revising 'create_and_delete_profile_cluster' scenario to make it benchmark cluster creating/deleting only; - Revising rally-senlin test job and example jobs accordingly; - Minor fix SenlinMixin cleanup resource to make id query work correctly; - Removing admin requirement from profile util functions since profile creating/deleting doesn't need admin role. Partial-bp: add-support-for-clustering-service-senlin Change-Id: Ib74803d13f4f89152af3a1ff5e7b6ff9fbac2906
This commit is contained in:
parent
9312a0fdab
commit
d701c6317a
@ -1,16 +1,7 @@
|
||||
---
|
||||
SenlinClusters.create_and_delete_profile_cluster:
|
||||
SenlinClusters.create_and_delete_cluster:
|
||||
-
|
||||
args:
|
||||
profile_spec:
|
||||
type: os.nova.server
|
||||
version: 1.0
|
||||
properties:
|
||||
name: cirros_server
|
||||
flavor: 1
|
||||
image: "cirros-0.3.4-x86_64-uec"
|
||||
networks:
|
||||
- network: private
|
||||
desired_capacity: 3
|
||||
min_size: 0
|
||||
max_size: 5
|
||||
@ -22,6 +13,15 @@
|
||||
users:
|
||||
tenants: 2
|
||||
users_per_tenant: 2
|
||||
profiles:
|
||||
type: os.nova.server
|
||||
version: "1.0"
|
||||
properties:
|
||||
name: cirros_server
|
||||
flavor: 1
|
||||
image: "cirros-0.3.4-x86_64-uec"
|
||||
networks:
|
||||
- network: private
|
||||
sla:
|
||||
failure_rate:
|
||||
max: 0
|
||||
|
@ -82,6 +82,9 @@ _senlin_order = get_order(150)
|
||||
@base.resource(service=None, resource=None, admin_required=True)
|
||||
class SenlinMixin(base.ResourceManager):
|
||||
|
||||
def id(self):
|
||||
return self.raw_resource["id"]
|
||||
|
||||
def _manager(self):
|
||||
client = self._admin_required and self.admin or self.user
|
||||
return getattr(client, self._service)()
|
||||
@ -92,7 +95,7 @@ class SenlinMixin(base.ResourceManager):
|
||||
def delete(self):
|
||||
# make singular form of resource name from plural form
|
||||
res_name = self._resource[:-1]
|
||||
return getattr(self._manager(), "delete_%s" % res_name)(self.id)
|
||||
return getattr(self._manager(), "delete_%s" % res_name)(self.id())
|
||||
|
||||
|
||||
@base.resource("senlin", "clusters", order=next(_senlin_order))
|
||||
@ -100,7 +103,8 @@ class SenlinCluster(SenlinMixin):
|
||||
"""Resource class for Senlin Cluster."""
|
||||
|
||||
|
||||
@base.resource("senlin", "profiles", order=next(_senlin_order))
|
||||
@base.resource("senlin", "profiles", order=next(_senlin_order),
|
||||
admin_required=False, tenant_resource=True)
|
||||
class SenlinProfile(SenlinMixin):
|
||||
"""Resource class for Senlin Profile."""
|
||||
|
||||
|
0
rally/plugins/openstack/context/senlin/__init__.py
Normal file
0
rally/plugins/openstack/context/senlin/__init__.py
Normal file
76
rally/plugins/openstack/context/senlin/profiles.py
Normal file
76
rally/plugins/openstack/context/senlin/profiles.py
Normal file
@ -0,0 +1,76 @@
|
||||
# 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 rally.common.i18n import _
|
||||
from rally.common import logging
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
from rally.plugins.openstack.scenarios.senlin import utils as senlin_utils
|
||||
from rally.task import context
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.configure(name="profiles", order=190)
|
||||
class ProfilesGenerator(context.Context):
|
||||
"""Context creates a temporary profile for Senlin test."""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["type", "version", "properties"]
|
||||
}
|
||||
|
||||
@logging.log_task_wrapper(LOG.info, _("Enter context: `Senlin profiles`"))
|
||||
def setup(self):
|
||||
"""Create test profiles."""
|
||||
for user, tenant_id in rutils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
|
||||
senlin_scenario = senlin_utils.SenlinScenario({
|
||||
"user": user,
|
||||
"task": self.context["task"],
|
||||
"config": {
|
||||
"api_versions": self.context["config"].get(
|
||||
"api_versions", [])}
|
||||
})
|
||||
profile = senlin_scenario._create_profile(self.config)
|
||||
|
||||
self.context["tenants"][tenant_id]["profile"] = profile.id
|
||||
|
||||
@logging.log_task_wrapper(LOG.info, _("Exit context: `Senlin profiles`"))
|
||||
def cleanup(self):
|
||||
"""Delete created test profiles."""
|
||||
for user, tenant_id in rutils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
|
||||
senlin_scenario = senlin_utils.SenlinScenario({
|
||||
"user": user,
|
||||
"task": self.context["task"],
|
||||
"config": {
|
||||
"api_versions": self.context["config"].get(
|
||||
"api_versions", [])}
|
||||
})
|
||||
senlin_scenario._delete_profile(
|
||||
self.context["tenants"][tenant_id]["profile"])
|
@ -21,18 +21,15 @@ class SenlinClusters(utils.SenlinScenario):
|
||||
|
||||
@validation.required_openstack(admin=True)
|
||||
@validation.required_services(consts.Service.SENLIN)
|
||||
@validation.required_contexts("profiles")
|
||||
@scenario.configure(context={"cleanup": ["senlin"]})
|
||||
def create_and_delete_profile_cluster(self, profile_spec,
|
||||
desired_capacity=0, min_size=0,
|
||||
max_size=-1, timeout=3600,
|
||||
metadata=None):
|
||||
"""Create a profile and a cluster and then delete them.
|
||||
def create_and_delete_cluster(self, desired_capacity=0, min_size=0,
|
||||
max_size=-1, timeout=3600, metadata=None):
|
||||
"""Create a cluster and then delete it.
|
||||
|
||||
Measure the "senlin profile-create", "senlin profile-delete",
|
||||
"senlin cluster-create" and "senlin cluster-delete" commands
|
||||
performance.
|
||||
Measure the "senlin cluster-create" and "senlin cluster-delete"
|
||||
commands performance.
|
||||
|
||||
:param profile_spec: spec dictionary used to create profile
|
||||
:param desired_capacity: The capacity or initial number of nodes
|
||||
owned by the cluster
|
||||
:param min_size: The minimum number of nodes owned by the cluster
|
||||
@ -41,8 +38,7 @@ class SenlinClusters(utils.SenlinScenario):
|
||||
:param timeout: The timeout value in seconds for cluster creation
|
||||
:param metadata: A set of key value pairs to associate with the cluster
|
||||
"""
|
||||
profile = self._create_profile(profile_spec)
|
||||
cluster = self._create_cluster(profile.id, desired_capacity,
|
||||
profile_id = self.context["tenant"]["profile"]
|
||||
cluster = self._create_cluster(profile_id, desired_capacity,
|
||||
min_size, max_size, timeout, metadata)
|
||||
self._delete_cluster(cluster)
|
||||
self._delete_profile(profile)
|
||||
|
@ -140,7 +140,7 @@ class SenlinScenario(scenario.OpenStackScenario):
|
||||
if metadata:
|
||||
attrs["metadata"] = metadata
|
||||
|
||||
return self.admin_clients("senlin").create_profile(**attrs)
|
||||
return self.clients("senlin").create_profile(**attrs)
|
||||
|
||||
@atomic.action_timer("senlin.delete_profile")
|
||||
def _delete_profile(self, profile):
|
||||
@ -150,4 +150,4 @@ class SenlinScenario(scenario.OpenStackScenario):
|
||||
|
||||
:param profile: profile object to be deleted
|
||||
"""
|
||||
self.admin_clients("senlin").delete_profile(profile)
|
||||
self.clients("senlin").delete_profile(profile)
|
||||
|
@ -1,19 +1,7 @@
|
||||
{
|
||||
"SenlinClusters.create_and_delete_profile_cluster": [
|
||||
"SenlinClusters.create_and_delete_cluster": [
|
||||
{
|
||||
"args": {
|
||||
"profile_spec": {
|
||||
"type": "os.nova.server",
|
||||
"version": "1.0",
|
||||
"properties": {
|
||||
"name": "cirros_server",
|
||||
"flavor": 1,
|
||||
"image": "cirros-0.3.4-x86_64-uec",
|
||||
"networks": [
|
||||
{ "network": "private" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"desired_capacity": 3,
|
||||
"min_size": 0,
|
||||
"max_size": 5
|
||||
@ -27,6 +15,18 @@
|
||||
"users": {
|
||||
"tenants": 1,
|
||||
"users_per_tenant": 1
|
||||
},
|
||||
"profiles": {
|
||||
"type": "os.nova.server",
|
||||
"version": "1.0",
|
||||
"properties": {
|
||||
"name": "cirros_server",
|
||||
"flavor": 1,
|
||||
"image": "cirros-0.3.4-x86_64-uec",
|
||||
"networks": [
|
||||
{ "network": "private" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,7 @@
|
||||
---
|
||||
SenlinClusters.create_and_delete_profile_cluster:
|
||||
SenlinClusters.create_and_delete_cluster:
|
||||
-
|
||||
args:
|
||||
profile_spec:
|
||||
type: os.nova.server
|
||||
version: "1.0"
|
||||
properties:
|
||||
name: cirros_server
|
||||
flavor: 1
|
||||
image: "cirros-0.3.4-x86_64-uec"
|
||||
networks:
|
||||
- network: private
|
||||
desired_capacity: 3
|
||||
min_size: 0
|
||||
max_size: 5
|
||||
@ -22,3 +13,12 @@
|
||||
users:
|
||||
tenants: 1
|
||||
users_per_tenant: 1
|
||||
profiles:
|
||||
type: os.nova.server
|
||||
version: "1.0"
|
||||
properties:
|
||||
name: cirros_server
|
||||
flavor: 1
|
||||
image: "cirros-0.3.4-x86_64-uec"
|
||||
networks:
|
||||
- network: private
|
||||
|
@ -758,6 +758,11 @@ class FuelEnvironmentTestCase(test.TestCase):
|
||||
|
||||
class SenlinMixinTestCase(test.TestCase):
|
||||
|
||||
def test_id(self):
|
||||
senlin = resources.SenlinMixin()
|
||||
senlin.raw_resource = {"id": "TEST_ID"}
|
||||
self.assertEqual("TEST_ID", senlin.id())
|
||||
|
||||
def test__manager(self):
|
||||
senlin = resources.SenlinMixin()
|
||||
senlin._service = "senlin"
|
||||
@ -781,7 +786,7 @@ class SenlinMixinTestCase(test.TestCase):
|
||||
senlin._service = "senlin"
|
||||
senlin.user = mock.MagicMock()
|
||||
senlin._resource = "some_resources"
|
||||
senlin.id = "TEST_ID"
|
||||
senlin.raw_resource = {"id": "TEST_ID"}
|
||||
senlin.user.senlin().delete_some_resource.return_value = None
|
||||
|
||||
senlin.delete()
|
||||
|
84
tests/unit/plugins/openstack/context/senlin/test_profiles.py
Normal file
84
tests/unit/plugins/openstack/context/senlin/test_profiles.py
Normal file
@ -0,0 +1,84 @@
|
||||
# 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 mock
|
||||
|
||||
from rally.plugins.openstack.context.senlin import profiles
|
||||
from tests.unit import test
|
||||
|
||||
|
||||
BASE_CTX = "rally.task.context"
|
||||
CTX = "rally.plugins.openstack.context"
|
||||
BASE_SCN = "rally.task.scenarios"
|
||||
SCN = "rally.plugins.openstack.scenarios"
|
||||
|
||||
|
||||
class ProfilesGeneratorTestCase(test.ScenarioTestCase):
|
||||
"""Generate tenants."""
|
||||
def _gen_tenants(self, count):
|
||||
tenants = {}
|
||||
for _id in range(count):
|
||||
tenants[str(_id)] = {"id": str(_id)}
|
||||
return tenants
|
||||
|
||||
def setUp(self):
|
||||
super(ProfilesGeneratorTestCase, self).setUp()
|
||||
self.tenants_count = 2
|
||||
self.users_per_tenant = 3
|
||||
tenants = self._gen_tenants(self.tenants_count)
|
||||
users = []
|
||||
for tenant in tenants:
|
||||
for i in range(self.users_per_tenant):
|
||||
users.append({"id": i, "tenant_id": tenant,
|
||||
"credential": mock.MagicMock()})
|
||||
|
||||
self.context = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": self.tenants_count,
|
||||
"users_per_tenant": self.users_per_tenant
|
||||
},
|
||||
"profiles": {
|
||||
"type": "profile_type_name",
|
||||
"version": "1.0",
|
||||
"properties": {"k1": "v1", "k2": "v2"}
|
||||
},
|
||||
},
|
||||
"users": users,
|
||||
"tenants": tenants,
|
||||
"task": mock.MagicMock()
|
||||
}
|
||||
|
||||
@mock.patch("%s.senlin.utils.SenlinScenario._create_profile" % SCN,
|
||||
return_value=mock.MagicMock(id="TEST_PROFILE_ID"))
|
||||
def test_setup(self, mock_senlin_scenario__create_profile):
|
||||
profile_ctx = profiles.ProfilesGenerator(self.context)
|
||||
profile_ctx.setup()
|
||||
spec = self.context["config"]["profiles"]
|
||||
|
||||
mock_calls = [mock.call(spec) for i in range(self.tenants_count)]
|
||||
mock_senlin_scenario__create_profile.assert_has_calls(mock_calls)
|
||||
|
||||
for tenant in self.context["tenants"]:
|
||||
self.assertEqual("TEST_PROFILE_ID",
|
||||
self.context["tenants"][tenant]["profile"])
|
||||
|
||||
@mock.patch("%s.senlin.utils.SenlinScenario._delete_profile" % SCN)
|
||||
def test_cleanup(self, mock_senlin_scenario__delete_profile):
|
||||
for tenant in self.context["tenants"]:
|
||||
self.context["tenants"][tenant].update(
|
||||
{"profile": "TEST_PROFILE_ID"})
|
||||
profile_ctx = profiles.ProfilesGenerator(self.context)
|
||||
profile_ctx.cleanup()
|
||||
mock_calls = [mock.call("TEST_PROFILE_ID") for i in range(
|
||||
self.tenants_count)]
|
||||
mock_senlin_scenario__delete_profile.assert_has_calls(mock_calls)
|
@ -19,25 +19,17 @@ from tests.unit import test
|
||||
class SenlinClustersTestCase(test.ScenarioTestCase):
|
||||
|
||||
def test_create_and_delete_cluster(self):
|
||||
profile_spec = {"k1": "v1"}
|
||||
mock_profile = mock.Mock(id="fake_profile_id")
|
||||
mock_cluster = mock.Mock()
|
||||
self.context["tenant"] = {"profile": "fake_profile_id"}
|
||||
scenario = clusters.SenlinClusters(self.context)
|
||||
scenario._create_cluster = mock.Mock(return_value=mock_cluster)
|
||||
scenario._create_profile = mock.Mock(return_value=mock_profile)
|
||||
scenario._delete_cluster = mock.Mock()
|
||||
scenario._delete_profile = mock.Mock()
|
||||
|
||||
scenario.create_and_delete_profile_cluster(profile_spec,
|
||||
desired_capacity=1,
|
||||
min_size=0,
|
||||
max_size=3,
|
||||
timeout=60,
|
||||
scenario.create_and_delete_cluster(desired_capacity=1, min_size=0,
|
||||
max_size=3, timeout=60,
|
||||
metadata={"k2": "v2"})
|
||||
|
||||
scenario._create_profile.assert_called_once_with(profile_spec)
|
||||
scenario._create_cluster.assert_called_once_with("fake_profile_id",
|
||||
1, 0, 3, 60,
|
||||
{"k2": "v2"})
|
||||
scenario._delete_cluster.assert_called_once_with(mock_cluster)
|
||||
scenario._delete_profile.assert_called_once_with(mock_profile)
|
||||
|
@ -134,8 +134,8 @@ class SenlinScenarioTestCase(test.ScenarioTestCase):
|
||||
result = scenario._create_profile(test_spec, metadata={"k2": "v2"})
|
||||
|
||||
self.assertEqual(
|
||||
self.admin_clients("senlin").create_profile.return_value, result)
|
||||
self.admin_clients("senlin").create_profile.assert_called_once_with(
|
||||
self.clients("senlin").create_profile.return_value, result)
|
||||
self.clients("senlin").create_profile.assert_called_once_with(
|
||||
spec=test_spec, name="test_profile", metadata={"k2": "v2"})
|
||||
mock_generate_random_name.assert_called_once_with()
|
||||
self._test_atomic_action_timer(scenario.atomic_actions(),
|
||||
@ -146,7 +146,7 @@ class SenlinScenarioTestCase(test.ScenarioTestCase):
|
||||
scenario = utils.SenlinScenario(context=self.context)
|
||||
scenario._delete_profile(fake_profile)
|
||||
|
||||
self.admin_clients("senlin").delete_profile.assert_called_once_with(
|
||||
self.clients("senlin").delete_profile.assert_called_once_with(
|
||||
fake_profile)
|
||||
self._test_atomic_action_timer(scenario.atomic_actions(),
|
||||
"senlin.delete_profile")
|
||||
|
Loading…
Reference in New Issue
Block a user