[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:
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:
-
runner:
@ -306,6 +326,13 @@
users:
tenants: 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:
failure_rate:
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 import validation
from rally import consts
from rally import exceptions
class CeilometerResource(ceilometerutils.CeilometerScenario):
@ -29,4 +30,21 @@ class CeilometerResource(ceilometerutils.CeilometerScenario):
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()
@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")
def _get_stats(self, meter_name):
"""Get stats for a specific meter.
@ -191,7 +196,7 @@ class CeilometerScenario(base.Scenario):
@base.atomic_action_timer("ceilometer.create_sample")
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.
:param counter_name: specifies name of the counter
@ -206,7 +211,9 @@ class CeilometerScenario(base.Scenario):
"counter_type": counter_type,
"counter_unit": counter_unit,
"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)
@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": {
"tenants": 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:
tenants: 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
from rally.benchmark.scenarios.ceilometer import resources
from rally import exceptions
from tests.unit import test
@ -23,5 +24,24 @@ class CeilometerResourcesTestCase(test.TestCase):
scenario = resources.CeilometerResource()
scenario._list_resources = mock.MagicMock()
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._list_samples = mock.MagicMock()
scenario.list_samples()
scenario._list_samples.assert_called_once_with()

View File

@ -122,6 +122,10 @@ class CeilometerScenarioTestCase(test.TestCase):
fake_samples = self.scenario._list_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):
"""Test _get_stats function."""
fake_statistics = self.scenario._get_stats("fake-meter")

View File

@ -219,6 +219,7 @@ class FakeAlarm(FakeResource):
self.threshold = kwargs.get("threshold")
self.state = kwargs.get("state", "fake-alarm-state")
self.alarm_id = kwargs.get("alarm_id", "fake-alarm-id")
self.state = kwargs.get("state", "ok")
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_unit = kwargs.get("counter_unit", "fake-counter-unit")
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):
@ -775,6 +786,9 @@ class FakeMeterManager(FakeManager):
class FakeCeilometerResourceManager(FakeManager):
def get(self, resource_id):
return ["fake-resource-info"]
def list(self):
return ["fake-resource"]