[Ceilometer] Add context and scenario for resources

- Added context ceilometer.py for creating samples
and collecting resources in list
- Added CeilometerResource.get_resources_on_tenant scenario

Change-Id: I4cf6148cdd3fa685a2ae17eb16f1e205e1edb2bf
This commit is contained in:
vgusev 2015-03-17 17:27:16 +03:00
parent 5c6735c073
commit d663c1ed96
13 changed files with 372 additions and 6 deletions

View File

@ -296,6 +296,26 @@
failure_rate: failure_rate:
max: 0 max: 0
CeilometerResource.get_tenant_resources:
-
runner:
type: "constant"
times: 10
concurrency: 5
context:
users:
tenants: 2
users_per_tenant: 2
ceilometer:
counter_name: "cpu_util"
counter_type: "gauge"
counter_volume: 1.0
counter_unit: "instance"
resources_per_tenant: 3
sla:
failure_rate:
max: 0
CeilometerSamples.list_samples: CeilometerSamples.list_samples:
- -
runner: runner:
@ -306,6 +326,13 @@
users: users:
tenants: 1 tenants: 1
users_per_tenant: 1 users_per_tenant: 1
ceilometer:
counter_name: "cpu_util"
counter_type: "gauge"
counter_unit: "instance"
counter_volume: 1.0
resources_per_tenant: 3
samples_per_resource: 3
sla: sla:
failure_rate: failure_rate:
max: 0 max: 0

View File

@ -0,0 +1,100 @@
# 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.benchmark.context import base
from rally.benchmark.scenarios.ceilometer import utils as ceilometer_utils
from rally.common.i18n import _
from rally.common import log as logging
from rally.common import utils as rutils
from rally import consts
from rally import osclients
LOG = logging.getLogger(__name__)
@base.context(name="ceilometer", order=450)
class CeilometerSampleGenerator(base.Context):
"""Context for creating samples and collecting resources for benchmarks."""
CONFIG_SCHEMA = {
"type": "object",
"$schema": consts.JSON_SCHEMA,
"properties": {
"counter_name": {
"type": "string"
},
"counter_type": {
"type": "string"
},
"counter_unit": {
"type": "string"
},
"counter_volume": {
"type": "number",
"minimum": 0
},
"resources_per_tenant": {
"type": "integer",
"minimum": 1
},
"samples_per_resource": {
"type": "integer",
"minimum": 1
},
},
"required": ["counter_name", "counter_type", "counter_unit",
"counter_volume"],
"additionalProperties": False
}
DEFAULT_CONFIG = {
"resources_per_tenant": 5,
"samples_per_resource": 5
}
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Ceilometer`"))
def setup(self):
counter_name = self.config["counter_name"]
counter_type = self.config["counter_type"]
counter_unit = self.config["counter_unit"]
counter_volume = self.config["counter_volume"]
resources_per_tenant = self.config["resources_per_tenant"]
samples_per_resource = self.config["samples_per_resource"]
for user, tenant_id in rutils.iterate_per_tenants(
self.context["users"]):
self.context["tenants"][tenant_id]["samples"] = []
self.context["tenants"][tenant_id]["resources"] = []
clients = osclients.Clients(user["endpoint"])
scenario = ceilometer_utils.CeilometerScenario(
clients=clients)
for i in range(resources_per_tenant):
for j in range(samples_per_resource):
try:
sample = scenario._create_sample(counter_name,
counter_type,
counter_unit,
counter_volume)
self.context["tenants"][tenant_id]["samples"].append(
sample[0].to_dict())
except Exception as e:
LOG.error("Creating a sample failed: %(error)s"
% {"error": e})
self.context["tenants"][tenant_id]["resources"].append(
sample[0].resource_id)
@rutils.log_task_wrapper(LOG.info, _("Exit context: `Ceilometer`"))
def cleanup(self):
# We don't have API for removal of samples and resources
pass

View File

@ -16,6 +16,7 @@ from rally.benchmark.scenarios import base
from rally.benchmark.scenarios.ceilometer import utils as ceilometerutils from rally.benchmark.scenarios.ceilometer import utils as ceilometerutils
from rally.benchmark import validation from rally.benchmark import validation
from rally import consts from rally import consts
from rally import exceptions
class CeilometerResource(ceilometerutils.CeilometerScenario): class CeilometerResource(ceilometerutils.CeilometerScenario):
@ -29,4 +30,21 @@ class CeilometerResource(ceilometerutils.CeilometerScenario):
This scenario fetches list of all resources using GET /v2/resources. This scenario fetches list of all resources using GET /v2/resources.
""" """
self._list_resources() self._list_resources()
@validation.required_services(consts.Service.CEILOMETER)
@validation.required_openstack(users=True)
@base.scenario()
def get_tenant_resources(self):
"""Get all tenant resources.
This scenario retrieves information about tenant resources using
GET /v2/resources/(resource_id)
"""
resources = self.context["tenant"].get("resources", [])
if not resources:
msg = ("No resources found for tenant: %s"
% self.context["tenant"].get("name"))
raise exceptions.NotFoundException(message=msg)
for res_id in resources:
self._get_resource(res_id)

View File

@ -138,6 +138,11 @@ class CeilometerScenario(base.Scenario):
""" """
return self.clients("ceilometer").samples.list() return self.clients("ceilometer").samples.list()
@base.atomic_action_timer("ceilometer.get_resource")
def _get_resource(self, resource_id):
"""Retrieve details about one resource."""
return self.clients("ceilometer").resources.get(resource_id)
@base.atomic_action_timer("ceilometer.get_stats") @base.atomic_action_timer("ceilometer.get_stats")
def _get_stats(self, meter_name): def _get_stats(self, meter_name):
"""Get stats for a specific meter. """Get stats for a specific meter.
@ -191,7 +196,7 @@ class CeilometerScenario(base.Scenario):
@base.atomic_action_timer("ceilometer.create_sample") @base.atomic_action_timer("ceilometer.create_sample")
def _create_sample(self, counter_name, counter_type, counter_unit, def _create_sample(self, counter_name, counter_type, counter_unit,
counter_volume, resource_id, **kwargs): counter_volume, resource_id=None, **kwargs):
"""Create a Sample with specified parameters. """Create a Sample with specified parameters.
:param counter_name: specifies name of the counter :param counter_name: specifies name of the counter
@ -206,7 +211,9 @@ class CeilometerScenario(base.Scenario):
"counter_type": counter_type, "counter_type": counter_type,
"counter_unit": counter_unit, "counter_unit": counter_unit,
"counter_volume": counter_volume, "counter_volume": counter_volume,
"resource_id": resource_id}) "resource_id": resource_id if resource_id
else self._generate_random_name(
prefix="rally_ctx_resource_")})
return self.clients("ceilometer").samples.create(**kwargs) return self.clients("ceilometer").samples.create(**kwargs)
@base.atomic_action_timer("ceilometer.query_samples") @base.atomic_action_timer("ceilometer.query_samples")

View File

@ -0,0 +1,23 @@
{
"CeilometerResource.get_tenant_resources": [
{
"runner": {
"type": "constant",
"times": 10,
"concurrency": 5
},
"context": {
"users": {
"tenants": 2,
"users_per_tenant": 2
},
"ceilometer": {
"counter_name": "cpu_util",
"counter_type": "gauge",
"counter_unit": "instance",
"counter_volume": 1.0
}
}
}
]
}

View File

@ -0,0 +1,16 @@
---
CeilometerResource.get_tenant_resources:
-
runner:
type: "constant"
times: 10
concurrency: 5
context:
users:
tenants: 2
users_per_tenant: 2
ceilometer:
counter_name: "cpu_util"
counter_type: "gauge"
counter_volume: 1.0
counter_unit: "instance"

View File

@ -10,6 +10,14 @@
"users": { "users": {
"tenants": 2, "tenants": 2,
"users_per_tenant": 2 "users_per_tenant": 2
},
"ceilometer": {
"counter_name": "cpu_util",
"counter_type": "gauge",
"counter_unit": "instance",
"counter_volume": 1.0,
"resources_per_tenant": 3,
"samples_per_resource": 3
} }
} }
} }

View File

@ -9,3 +9,10 @@
users: users:
tenants: 2 tenants: 2
users_per_tenant: 2 users_per_tenant: 2
ceilometer:
counter_name: "cpu_util"
counter_type: "gauge"
counter_unit: "instance"
counter_volume: 1.0
resources_per_tenant: 3
samples_per_resource: 3

View File

@ -0,0 +1,123 @@
# 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 mock
from rally.benchmark.context import ceilometer
from tests.unit import fakes
from tests.unit import test
CTX = "rally.benchmark.context"
SCN = "rally.benchmark.scenarios"
class CeilometerSampleGeneratorTestCase(test.TestCase):
def _gen_tenants(self, count):
tenants = {}
for id in range(count):
tenants[str(id)] = dict(name=str(id))
return tenants
def _gen_context(self, tenants_count, users_per_tenant,
resources_per_tenant, samples_per_resource):
tenants = self._gen_tenants(tenants_count)
users = []
for id_ in tenants.keys():
for i in range(users_per_tenant):
users.append({"id": i, "tenant_id": id_,
"endpoint": "endpoint"})
context = {
"config": {
"users": {
"tenants": tenants_count,
"users_per_tenant": users_per_tenant,
"concurrent": 10,
},
"ceilometer": {
"counter_name": "fake-counter-name",
"counter_type": "fake-counter-type",
"counter_unit": "fake-counter-unit",
"counter_volume": 100,
"resources_per_tenant": resources_per_tenant,
"samples_per_resource": samples_per_resource
}
},
"admin": {
"endpoint": mock.MagicMock()
},
"task": mock.MagicMock(),
"users": users,
"tenants": tenants
}
return tenants, context
def test_init(self):
context = {}
context["task"] = mock.MagicMock()
context["config"] = {
"ceilometer": {
"counter_name": "cpu_util",
"counter_type": "gauge",
"counter_unit": "instance",
"counter_volume": 1.0,
"resources_per_tenant": 5,
"samples_per_resource": 5
}
}
inst = ceilometer.CeilometerSampleGenerator(context)
self.assertEqual(inst.config, context["config"]["ceilometer"])
@mock.patch("%s.ceilometer.osclients" % CTX)
def test_setup(self, mock_osclients):
fc = fakes.FakeClients()
mock_osclients.Clients.return_value = fc
tenants_count = 2
users_per_tenant = 5
resources_per_tenant = 5
samples_per_resource = 5
tenants, real_context = self._gen_context(
tenants_count, users_per_tenant,
resources_per_tenant, samples_per_resource)
new_context = copy.deepcopy(real_context)
for id_ in tenants.keys():
new_context["tenants"][id_].setdefault("samples", list())
new_context["tenants"][id_].setdefault("resources", list())
for i in range(resources_per_tenant):
for j in range(samples_per_resource):
new_context["tenants"][id_]["samples"].append(
{"counter_name": "fake-counter-name",
"counter_type": "fake-counter-type",
"counter_unit": "fake-counter-unit",
"counter_volume": 100,
"resource_id": "fake-resource-id"})
new_context["tenants"][id_]["resources"].append(
"fake-resource-id")
ceilometer_ctx = ceilometer.CeilometerSampleGenerator(real_context)
ceilometer_ctx.setup()
self.assertEqual(new_context, ceilometer_ctx.context)
@mock.patch("%s.ceilometer.osclients" % CTX)
def test_cleanup(self, mock_osclients):
tenants, context = self._gen_context(2, 5, 3, 3)
ceilometer_ctx = ceilometer.CeilometerSampleGenerator(context)
ceilometer_ctx.cleanup()

View File

@ -15,6 +15,7 @@
import mock import mock
from rally.benchmark.scenarios.ceilometer import resources from rally.benchmark.scenarios.ceilometer import resources
from rally import exceptions
from tests.unit import test from tests.unit import test
@ -23,5 +24,24 @@ class CeilometerResourcesTestCase(test.TestCase):
scenario = resources.CeilometerResource() scenario = resources.CeilometerResource()
scenario._list_resources = mock.MagicMock() scenario._list_resources = mock.MagicMock()
scenario.list_resources() scenario.list_resources()
scenario._list_resources.assert_called_once_with()
scenario._list_resources.assert_called_once_with() def test_get_tenant_resources(self):
scenario = resources.CeilometerResource()
resource_list = ["id1", "id2", "id3", "id4"]
context = {"user": {"tenant_id": "fake"},
"tenant": {"id": "fake", "resources": resource_list}}
scenario.context = context
scenario._get_resource = mock.MagicMock()
scenario.get_tenant_resources()
for resource_id in resource_list:
scenario._get_resource.assert_any_call(resource_id)
def test_get_tenant_resources_with_exception(self):
scenario = resources.CeilometerResource()
resource_list = []
context = {"user": {"tenant_id": "fake"},
"tenant": {"id": "fake", "resources": resource_list}}
scenario.context = context
self.assertRaises(exceptions.NotFoundException,
scenario.get_tenant_resources)

View File

@ -23,5 +23,4 @@ class CeilometerSamplesTestCase(test.TestCase):
scenario = samples.CeilometerSamples() scenario = samples.CeilometerSamples()
scenario._list_samples = mock.MagicMock() scenario._list_samples = mock.MagicMock()
scenario.list_samples() scenario.list_samples()
scenario._list_samples.assert_called_once_with() scenario._list_samples.assert_called_once_with()

View File

@ -122,6 +122,10 @@ class CeilometerScenarioTestCase(test.TestCase):
fake_samples = self.scenario._list_samples() fake_samples = self.scenario._list_samples()
self.assertEqual(fake_samples, ["fake-samples"]) self.assertEqual(fake_samples, ["fake-samples"])
def test__get_resource(self):
fake_resource_info = self.scenario._get_resource("fake-resource-id")
self.assertEqual(fake_resource_info, ["fake-resource-info"])
def test__get_stats(self): def test__get_stats(self):
"""Test _get_stats function.""" """Test _get_stats function."""
fake_statistics = self.scenario._get_stats("fake-meter") fake_statistics = self.scenario._get_stats("fake-meter")

View File

@ -219,6 +219,7 @@ class FakeAlarm(FakeResource):
self.threshold = kwargs.get("threshold") self.threshold = kwargs.get("threshold")
self.state = kwargs.get("state", "fake-alarm-state") self.state = kwargs.get("state", "fake-alarm-state")
self.alarm_id = kwargs.get("alarm_id", "fake-alarm-id") self.alarm_id = kwargs.get("alarm_id", "fake-alarm-id")
self.state = kwargs.get("state", "ok")
self.optional_args = kwargs.get("optional_args", {}) self.optional_args = kwargs.get("optional_args", {})
@ -229,7 +230,17 @@ class FakeSample(FakeResource):
self.counter_type = kwargs.get("counter_type", "fake-counter-type") self.counter_type = kwargs.get("counter_type", "fake-counter-type")
self.counter_unit = kwargs.get("counter_unit", "fake-counter-unit") self.counter_unit = kwargs.get("counter_unit", "fake-counter-unit")
self.counter_volume = kwargs.get("counter_volume", 100) self.counter_volume = kwargs.get("counter_volume", 100)
self.resource_id = kwargs.get("resource_id", "fake-resource-id")
@property
def resource_id(self):
return "fake-resource-id"
def to_dict(self):
return {"counter_name": self.counter_name,
"counter_type": self.counter_type,
"counter_unit": self.counter_unit,
"counter_volume": self.counter_volume,
"resource_id": self.resource_id}
class FakeVolume(FakeResource): class FakeVolume(FakeResource):
@ -775,6 +786,9 @@ class FakeMeterManager(FakeManager):
class FakeCeilometerResourceManager(FakeManager): class FakeCeilometerResourceManager(FakeManager):
def get(self, resource_id):
return ["fake-resource-info"]
def list(self): def list(self):
return ["fake-resource"] return ["fake-resource"]