Remove Senlin

This removes all support for the retired
Senlin project that was deprecated in 3.0.0

Change-Id: I2d083fa65c535201a2c25b56cf2a2ae710983d2f
This commit is contained in:
Tobias Urdin
2025-03-26 10:04:57 +01:00
parent 9f85b9e16f
commit c6754c1df6
30 changed files with 3 additions and 854 deletions

View File

@@ -1,5 +0,0 @@
- job:
name: rally-task-senlin
parent: rally-task-at-devstack
vars:
rally_task: rally-jobs/senlin.yaml

View File

@@ -26,6 +26,8 @@ Removed
* Removed all support for the retired Murano project
* Removed all support for the retired Sahara project
* Removed all support for the retired Senlin project
[3.0.0] - 2024-05-23
--------------------

View File

@@ -818,10 +818,6 @@
# Number of cleanup threads to run (integer value)
#cleanup_threads = 20
# Time in seconds to wait for senlin action to finish. (floating point
# value)
#senlin_action_timeout = 3600
# Neutron create loadbalancer timeout (floating point value)
#neutron_create_loadbalancer_timeout = 500.0

View File

@@ -1,30 +0,0 @@
---
version: 2
title: Task for gate-rally-dsvm-senlin-rally-ubuntu-xenial-nv job
description: >
This task contains various scenarios for testing senlin plugins
subtasks:
-
title: SenlinClusters.create_and_delete_cluster tests
scenario:
SenlinClusters.create_and_delete_cluster:
desired_capacity: 3
min_size: 0
max_size: 5
runner:
constant:
times: 3
concurrency: 2
contexts:
users:
tenants: 2
users_per_tenant: 2
profiles:
type: os.nova.server
version: "1.0"
properties:
name: cirros_server
flavor: 1
image: "cirros-0.5.2-x86_64-disk"
networks:
- network: private

View File

@@ -26,7 +26,6 @@ from rally_openstack.common.cfg import nova
from rally_openstack.common.cfg import octavia
from rally_openstack.common.cfg import osclients
from rally_openstack.common.cfg import profiler
from rally_openstack.common.cfg import senlin
from rally_openstack.common.cfg import vm
from rally_openstack.common.cfg import watcher
@@ -48,7 +47,7 @@ def list_opts():
nova.OPTS, osclients.OPTS, profiler.OPTS,
vm.OPTS, glance.OPTS, watcher.OPTS, tempest.OPTS,
keystone_roles.OPTS, keystone_users.OPTS, cleanup.OPTS,
senlin.OPTS, neutron.OPTS, octavia.OPTS,
neutron.OPTS, octavia.OPTS,
osprofilerchart.OPTS):
for category, opt in l_opts.items():
opts.setdefault(category, [])

View File

@@ -1,23 +0,0 @@
# Copyright 2013: 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 rally.common import cfg
OPTS = {"openstack": [
cfg.FloatOpt("senlin_action_timeout",
default=3600,
deprecated_group="benchmark",
help="Time in seconds to wait for senlin action to finish.")
]}

View File

@@ -57,7 +57,6 @@ class _Service(utils.ImmutableMixin, utils.EnumMixin):
CEILOMETER = "ceilometer"
MONASCA = "monasca"
S3 = "s3"
SENLIN = "senlin"
TROVE = "trove"
SWIFT = "swift"
MISTRAL = "mistral"
@@ -82,7 +81,6 @@ class _ServiceType(utils.ImmutableMixin, utils.EnumMixin):
CLOUD = "cloudformation"
ORCHESTRATION = "orchestration"
IDENTITY = "identity"
CLUSTERING = "clustering"
COMPUTE = "compute"
NETWORK = "network"
DNS = "dns"
@@ -101,7 +99,6 @@ class _ServiceType(utils.ImmutableMixin, utils.EnumMixin):
def __init__(self):
self.__names = {
self.CLUSTERING: _Service.SENLIN,
self.COMPUTE: _Service.NOVA,
# unversioned endpoint of Cinder
self.BLOCK_STORAGE: _Service.CINDER,

View File

@@ -729,24 +729,6 @@ class Monasca(OSClient):
return client
@configure("senlin", default_version="1", default_service_type="clustering",
supported_versions=["1"])
class Senlin(OSClient):
"""Wrapper for SenlinClient which returns an authenticated native client.
"""
def create_client(self, version=None, service_type=None):
"""Return senlin client."""
from senlinclient import client as senlin
return senlin.Client(
self.choose_version(version),
**self._get_auth_info(project_name_key="project_name",
cacert_key="cert",
endpoint_type="interface"))
@configure("magnum", default_version="1", supported_versions=["1"],
default_service_type="container-infra",)
class Magnum(OSClient):

View File

@@ -99,42 +99,6 @@ class HeatStack(base.ResourceManager):
return self.raw_resource.stack_name
# SENLIN
_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)()
def list(self):
return getattr(self._manager(), self._resource)()
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())
@base.resource("senlin", "clusters",
admin_required=True, order=next(_senlin_order))
class SenlinCluster(SenlinMixin):
"""Resource class for Senlin Cluster."""
@base.resource("senlin", "profiles", order=next(_senlin_order),
admin_required=False, tenant_resource=True)
class SenlinProfile(SenlinMixin):
"""Resource class for Senlin Profile."""
# NOVA
_nova_order = get_order(200)

View File

@@ -1,65 +0,0 @@
# 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 import validation
from rally_openstack.common import consts
from rally_openstack.task import context
from rally_openstack.task.scenarios.senlin import utils as senlin_utils
@validation.add("required_platform", platform="openstack", users=True)
@context.configure(name="profiles", platform="openstack", order=190)
class ProfilesGenerator(context.OpenStackContext):
"""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": True,
}
},
"additionalProperties": False,
"required": ["type", "version", "properties"]
}
def setup(self):
"""Create test profiles."""
for user, tenant_id in self._iterate_per_tenants():
senlin_scenario = senlin_utils.SenlinScenario({
"user": user,
"task": self.context["task"]
})
profile = senlin_scenario._create_profile(self.config)
self.context["tenants"][tenant_id]["profile"] = profile.id
def cleanup(self):
"""Delete created test profiles."""
for user, tenant_id in self._iterate_per_tenants():
senlin_scenario = senlin_utils.SenlinScenario({
"user": user,
"task": self.context["task"]
})
senlin_scenario._delete_profile(
self.context["tenants"][tenant_id]["profile"])

View File

@@ -1,50 +0,0 @@
# 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.task import validation
from rally_openstack.common import consts
from rally_openstack.task import scenario
from rally_openstack.task.scenarios.senlin import utils
"""Scenarios for Senlin clusters."""
@validation.add("required_platform", platform="openstack", admin=True)
@validation.add("required_services", services=[consts.Service.SENLIN])
@validation.add("required_contexts", contexts=("profiles"))
@scenario.configure(context={"admin_cleanup@openstack": ["senlin"]},
name="SenlinClusters.create_and_delete_cluster",
platform="openstack")
class CreateAndDeleteCluster(utils.SenlinScenario):
def run(self, desired_capacity=0, min_size=0,
max_size=-1, timeout=3600, metadata=None):
"""Create a cluster and then delete it.
Measure the "senlin cluster-create" and "senlin cluster-delete"
commands performance.
: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
:param max_size: The maximum number of nodes owned by the cluster.
-1 means no limit
:param timeout: The timeout value in seconds for cluster creation
:param metadata: A set of key value pairs to associate with the cluster
"""
profile_id = self.context["tenant"]["profile"]
cluster = self._create_cluster(profile_id, desired_capacity,
min_size, max_size, timeout, metadata)
self._delete_cluster(cluster)

View File

@@ -1,145 +0,0 @@
# 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 import cfg
from rally import exceptions
from rally.task import atomic
from rally.task import utils
from rally_openstack.task import scenario
CONF = cfg.CONF
class SenlinScenario(scenario.OpenStackScenario):
"""Base class for Senlin scenarios with basic atomic actions."""
@atomic.action_timer("senlin.list_clusters")
def _list_clusters(self, **queries):
"""Return user cluster list.
:param kwargs **queries: Optional query parameters to be sent to
restrict the clusters to be returned. Available parameters include:
* name: The name of a cluster.
* status: The current status of a cluster.
* sort: A list of sorting keys separated by commas. Each sorting
key can optionally be attached with a sorting direction
modifier which can be ``asc`` or ``desc``.
* limit: Requests a specified size of returned items from the
query. Returns a number of items up to the specified limit
value.
* marker: Specifies the ID of the last-seen item. Use the limit
parameter to make an initial limited request and use the ID of
the last-seen item from the response as the marker parameter
value in a subsequent limited request.
* global_project: A boolean value indicating whether clusters
from all projects will be returned.
:returns: list of clusters according to query.
"""
return list(self.admin_clients("senlin").clusters(**queries))
@atomic.action_timer("senlin.create_cluster")
def _create_cluster(self, profile_id, desired_capacity=0, min_size=0,
max_size=-1, timeout=60, metadata=None):
"""Create a new cluster from attributes.
:param profile_id: ID of profile used to create cluster
: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
:param max_size: The maximum number of nodes owned by the cluster.
-1 means no limit
:param timeout: The timeout value in minutes for cluster creation
:param metadata: A set of key value pairs to associate with the cluster
:returns: object of cluster created.
"""
attrs = {
"profile_id": profile_id,
"name": self.generate_random_name(),
"desired_capacity": desired_capacity,
"min_size": min_size,
"max_size": max_size,
"metadata": metadata,
"timeout": timeout
}
cluster = self.admin_clients("senlin").create_cluster(**attrs)
cluster = utils.wait_for_status(
cluster,
ready_statuses=["ACTIVE"],
failure_statuses=["ERROR"],
update_resource=self._get_cluster,
timeout=CONF.openstack.senlin_action_timeout)
return cluster
def _get_cluster(self, cluster):
"""Get cluster details.
:param cluster: cluster to get
:returns: object of cluster
"""
try:
return self.admin_clients("senlin").get_cluster(cluster.id)
except Exception as e:
if getattr(e, "code", getattr(e, "http_status", 400)) == 404:
raise exceptions.GetResourceNotFound(resource=cluster.id)
raise exceptions.GetResourceFailure(resource=cluster.id, err=e)
@atomic.action_timer("senlin.delete_cluster")
def _delete_cluster(self, cluster):
"""Delete given cluster.
Returns after the cluster is successfully deleted.
:param cluster: cluster object to delete
"""
self.admin_clients("senlin").delete_cluster(cluster)
utils.wait_for_status(
cluster,
ready_statuses=["DELETED"],
failure_statuses=["ERROR"],
check_deletion=True,
update_resource=self._get_cluster,
timeout=CONF.openstack.senlin_action_timeout)
@atomic.action_timer("senlin.create_profile")
def _create_profile(self, spec, metadata=None):
"""Create a new profile from attributes.
:param spec: spec dictionary used to create profile
:param metadata: A set of key value pairs to associate with the
profile
:returns: object of profile created
"""
attrs = {"spec": spec,
"name": self.generate_random_name()}
if metadata:
attrs["metadata"] = metadata
return self.clients("senlin").create_profile(**attrs)
@atomic.action_timer("senlin.delete_profile")
def _delete_profile(self, profile):
"""Delete given profile.
Returns after the profile is successfully deleted.
:param profile: profile object to be deleted
"""
self.clients("senlin").delete_profile(profile)

View File

@@ -18,7 +18,6 @@ from rally.common import utils
class _TempestApiTestSets(utils.ImmutableMixin, utils.EnumMixin):
BAREMETAL = "baremetal"
CLUSTERING = "clustering"
COMPUTE = "compute"
DATABASE = "database"
IDENTITY = "identity"

View File

@@ -25,7 +25,6 @@ python-monascaclient # Apache Software License
python-neutronclient # Apache Software License
python-novaclient # Apache License, Version 2.0
python-octaviaclient # Apache Software License
python-senlinclient # Apache Software License
python-swiftclient # Apache License, Version 2.0
python-troveclient # Apache Software License
python-watcherclient # Apache Software License

View File

@@ -1,32 +0,0 @@
{
"Dummy.openstack": [
{
"args": {
"sleep": 0.1
},
"runner": {
"type": "constant",
"times": 3,
"concurrency": 1
},
"context": {
"users": {
"tenants": 1,
"users_per_tenant": 1
},
"profiles": {
"type": "os.nova.server",
"version": "1.0",
"properties": {
"name": "cirros_server",
"flavor": 1,
"image": "cirros-0.5.2-x86_64-disk",
"networks": [
{ "network": "private" }
]
}
}
}
}
]
}

View File

@@ -1,22 +0,0 @@
---
Dummy.openstack:
-
args:
sleep: 0.1
runner:
type: "constant"
times: 3
concurrency: 1
context:
users:
tenants: 1
users_per_tenant: 1
profiles:
type: "os.nova.server"
version: "1.0"
properties:
name: "cirros_server"
flavor: 1
image: "cirros-0.5.2-x86_64-disk"
networks:
- network: "private"

View File

@@ -1,39 +0,0 @@
{
"SenlinClusters.create_and_delete_cluster": [
{
"args": {
"desired_capacity": 3,
"min_size": 0,
"max_size": 5
},
"runner": {
"type": "constant",
"times": 3,
"concurrency": 1
},
"context": {
"users": {
"tenants": 1,
"users_per_tenant": 1
},
"profiles": {
"type": "os.nova.server",
"version": "1.0",
"properties": {
"name": "cirros_server",
"flavor": 1,
"image": "cirros-0.5.2-x86_64-disk",
"networks": [
{ "network": "private" }
]
}
}
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@@ -1,27 +0,0 @@
---
SenlinClusters.create_and_delete_cluster:
-
args:
desired_capacity: 3
min_size: 0
max_size: 5
runner:
type: "constant"
times: 3
concurrency: 1
context:
users:
tenants: 1
users_per_tenant: 1
profiles:
type: os.nova.server
version: "1.0"
properties:
name: cirros_server
flavor: 1
image: "cirros-0.5.2-x86_64-disk"
networks:
- network: private
sla:
failure_rate:
max: 0

View File

@@ -279,17 +279,6 @@ class Cinder(ResourceManager):
return self.client.qos_specs.list()
class Senlin(ResourceManager):
REQUIRED_SERVICE = consts.Service.SENLIN
def list_clusters(self):
return self.client.clusters()
def list_profiles(self):
return self.client.profiles()
class Manila(ResourceManager):
REQUIRED_SERVICE = consts.Service.MANILA

View File

@@ -904,23 +904,6 @@ class OSClientsTestCase(test.TestCase):
key += "%s" % {"version": version}
self.assertEqual(fake_designate, self.clients.cache[key])
def test_senlin(self):
mock_senlin = mock.MagicMock()
self.assertNotIn("senlin", self.clients.cache)
with mock.patch.dict("sys.modules", {"senlinclient": mock_senlin}):
client = self.clients.senlin()
self.assertEqual(mock_senlin.client.Client.return_value, client)
mock_senlin.client.Client.assert_called_once_with(
"1",
username=self.credential.username,
password=self.credential.password,
project_name=self.credential.tenant_name,
cert=self.credential.cacert,
auth_url=self.credential.auth_url)
self.assertEqual(
mock_senlin.client.Client.return_value,
self.clients.cache["senlin"])
@mock.patch("%s.Magnum._get_endpoint" % PATH)
def test_magnum(self, mock_magnum__get_endpoint):
fake_magnum = fakes.FakeMagnumClient()

View File

@@ -1441,13 +1441,6 @@ class FakeEC2Client(object):
pass
class FakeSenlinClient(object):
def __init__(self):
# TODO(Yanyan Hu):Fake interfaces of senlinclient.
pass
class FakeMagnumClient(object):
def __init__(self):
@@ -1484,7 +1477,6 @@ class FakeClients(object):
self._swift = None
self._monasca = None
self._ec2 = None
self._senlin = None
self._watcher = None
self._barbican = None
self._credential = credential_ or FakeCredential(
@@ -1566,11 +1558,6 @@ class FakeClients(object):
self._ec2 = FakeEC2Client()
return self._ec2
def senlin(self):
if not self._senlin:
self._senlin = FakeSenlinClient()
return self._senlin
def watcher(self):
if not self._watcher:
self._watcher = FakeWatcherClient()

View File

@@ -911,44 +911,6 @@ class MistralExecutionsTestCase(test.TestCase):
self.assertEqual("bar", resources.MistralExecutions(execution).name())
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"
senlin.user = mock.MagicMock()
self.assertEqual(senlin.user.senlin.return_value, senlin._manager())
def test_list(self):
senlin = resources.SenlinMixin()
senlin._service = "senlin"
senlin.user = mock.MagicMock()
senlin._resource = "some_resources"
some_resources = [{"name": "resource1"}, {"name": "resource2"}]
senlin.user.senlin().some_resources.return_value = some_resources
self.assertEqual(some_resources, senlin.list())
senlin.user.senlin().some_resources.assert_called_once_with()
def test_delete(self):
senlin = resources.SenlinMixin()
senlin._service = "senlin"
senlin.user = mock.MagicMock()
senlin._resource = "some_resources"
senlin.raw_resource = {"id": "TEST_ID"}
senlin.user.senlin().delete_some_resource.return_value = None
senlin.delete()
senlin.user.senlin().delete_some_resource.assert_called_once_with(
"TEST_ID")
class WatcherTemplateTestCase(test.TestCase):
def test_id(self):

View File

@@ -1,84 +0,0 @@
# 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 unittest import mock
from rally_openstack.task.contexts.senlin import profiles
from tests.unit import test
BASE_CTX = "rally.task.context"
CTX = "rally_openstack.context"
BASE_SCN = "rally.task.scenarios"
SCN = "rally_openstack.task.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)

View File

@@ -1,34 +0,0 @@
# 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 unittest import mock
from rally_openstack.task.scenarios.senlin import clusters
from tests.unit import test
class SenlinClustersTestCase(test.ScenarioTestCase):
def test_create_and_delete_cluster(self):
mock_cluster = mock.Mock()
self.context["tenant"] = {"profile": "fake_profile_id"}
scenario = clusters.CreateAndDeleteCluster(self.context)
scenario._create_cluster = mock.Mock(return_value=mock_cluster)
scenario._delete_cluster = mock.Mock()
scenario.run(desired_capacity=1, min_size=0,
max_size=3, timeout=60, metadata={"k2": "v2"})
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)

View File

@@ -1,153 +0,0 @@
# 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 unittest import mock
from rally.common import cfg
from rally import exceptions
from rally_openstack.task.scenarios.senlin import utils
from tests.unit import test
SENLIN_UTILS = "rally_openstack.task.scenarios.senlin.utils."
CONF = cfg.CONF
class SenlinScenarioTestCase(test.ScenarioTestCase):
def test_list_cluster(self):
fake_cluster_list = ["cluster1", "cluster2"]
self.admin_clients("senlin").clusters.return_value = fake_cluster_list
scenario = utils.SenlinScenario(self.context)
result = scenario._list_clusters()
self.assertEqual(list(fake_cluster_list), result)
self.admin_clients("senlin").clusters.assert_called_once_with()
def test_list_cluster_with_queries(self):
fake_cluster_list = ["cluster1", "cluster2"]
self.admin_clients("senlin").clusters.return_value = fake_cluster_list
scenario = utils.SenlinScenario(self.context)
result = scenario._list_clusters(status="ACTIVE")
self.assertEqual(list(fake_cluster_list), result)
self.admin_clients("senlin").clusters.assert_called_once_with(
status="ACTIVE")
@mock.patch(SENLIN_UTILS + "SenlinScenario.generate_random_name",
return_value="test_cluster")
def test_create_cluster(self, mock_generate_random_name):
fake_cluster = mock.Mock(id="fake_cluster_id")
res_cluster = mock.Mock()
self.admin_clients("senlin").create_cluster.return_value = fake_cluster
self.mock_wait_for_status.mock.return_value = res_cluster
scenario = utils.SenlinScenario(self.context)
result = scenario._create_cluster("fake_profile_id",
desired_capacity=1,
min_size=0,
max_size=3,
metadata={"k1": "v1"},
timeout=60)
self.assertEqual(res_cluster, result)
self.admin_clients("senlin").create_cluster.assert_called_once_with(
profile_id="fake_profile_id", name="test_cluster",
desired_capacity=1, min_size=0, max_size=3, metadata={"k1": "v1"},
timeout=60)
self.mock_wait_for_status.mock.assert_called_once_with(
fake_cluster, ready_statuses=["ACTIVE"],
failure_statuses=["ERROR"],
update_resource=scenario._get_cluster,
timeout=CONF.openstack.senlin_action_timeout)
mock_generate_random_name.assert_called_once_with()
self._test_atomic_action_timer(scenario.atomic_actions(),
"senlin.create_cluster")
def test_get_cluster(self):
fake_cluster = mock.Mock(id="fake_cluster_id")
scenario = utils.SenlinScenario(context=self.context)
scenario._get_cluster(fake_cluster)
self.admin_clients("senlin").get_cluster.assert_called_once_with(
"fake_cluster_id")
def test_get_cluster_notfound(self):
fake_cluster = mock.Mock(id="fake_cluster_id")
ex = Exception()
ex.code = 404
self.admin_clients("senlin").get_cluster.side_effect = ex
scenario = utils.SenlinScenario(context=self.context)
self.assertRaises(exceptions.GetResourceNotFound,
scenario._get_cluster,
fake_cluster)
self.admin_clients("senlin").get_cluster.assert_called_once_with(
"fake_cluster_id")
def test_get_cluster_failed(self):
fake_cluster = mock.Mock(id="fake_cluster_id")
ex = Exception()
ex.code = 500
self.admin_clients("senlin").get_cluster.side_effect = ex
scenario = utils.SenlinScenario(context=self.context)
self.assertRaises(exceptions.GetResourceFailure,
scenario._get_cluster,
fake_cluster)
self.admin_clients("senlin").get_cluster.assert_called_once_with(
"fake_cluster_id")
def test_delete_cluster(self):
fake_cluster = mock.Mock()
scenario = utils.SenlinScenario(context=self.context)
scenario._delete_cluster(fake_cluster)
self.admin_clients("senlin").delete_cluster.assert_called_once_with(
fake_cluster)
self.mock_wait_for_status.mock.assert_called_once_with(
fake_cluster, ready_statuses=["DELETED"],
failure_statuses=["ERROR"], check_deletion=True,
update_resource=scenario._get_cluster,
timeout=CONF.openstack.senlin_action_timeout)
self._test_atomic_action_timer(scenario.atomic_actions(),
"senlin.delete_cluster")
@mock.patch(SENLIN_UTILS + "SenlinScenario.generate_random_name",
return_value="test_profile")
def test_create_profile(self, mock_generate_random_name):
test_spec = {
"version": "1.0",
"type": "test_type",
"properties": {
"key1": "value1"
}
}
scenario = utils.SenlinScenario(self.context)
result = scenario._create_profile(test_spec, metadata={"k2": "v2"})
self.assertEqual(
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(),
"senlin.create_profile")
def test_delete_profile(self):
fake_profile = mock.Mock()
scenario = utils.SenlinScenario(context=self.context)
scenario._delete_profile(fake_profile)
self.clients("senlin").delete_profile.assert_called_once_with(
fake_profile)
self._test_atomic_action_timer(scenario.atomic_actions(),
"senlin.delete_profile")

View File

@@ -82,7 +82,6 @@ python-neutronclient===11.2.0
python-novaclient===18.6.0
python-octaviaclient===3.7.0
python-openstackclient===6.6.0
python-senlinclient===3.1.0
python-subunit===1.4.4
python-swiftclient===4.5.0
python-troveclient===8.4.0