Remove monasca support

The monasca project is being retired due to lack of maintenance effort
even after the project was marked inactive.

PRAGMA: NO COVER
Change-Id: I6c2254e062f45e2554cbc18ec2f8f95cae73c83d
Signed-off-by: Takashi Kajinami <kajinamit@oss.nttdata.com>
This commit is contained in:
Takashi Kajinami
2025-08-14 17:29:00 +09:00
parent ef36680111
commit 284d08d562
31 changed files with 4 additions and 725 deletions

View File

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

View File

@@ -50,8 +50,6 @@
- rally-task-manila-ss:
voting: false
- rally-task-mistral
# it did not work for a long time. try to re-configure it
#- rally-task-monasca
- rally-task-neutron-trunk:
files:
- .zuul.d/zuul.yaml

View File

@@ -31,6 +31,7 @@ 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
* Removed all support for the retired Monasca project
Changed
~~~~~~~

View File

@@ -483,10 +483,6 @@
# mistral execution timeout (integer value)
#mistral_execution_timeout = 200
# Delay between creating Monasca metrics and polling for its elements.
# (floating point value)
#monasca_metric_create_prepoll_delay = 15.0
# Time to sleep after start before polling for status (floating point
# value)
#nova_server_start_prepoll_delay = 0.0

View File

@@ -1,44 +0,0 @@
---
version: 2
title: Task for gate-rally-dsvm-monasca-rally-ubuntu-xenial-nv job
description: >
This task contains various subtasks for testing Monasca plugins
subtasks:
-
title: MonascaMetrics.list_metrics tests
workloads:
-
scenario:
MonascaMetrics.list_metrics: {}
runner:
constant:
times: 10
concurrency: 2
contexts:
users:
tenants: 2
users_per_tenant: 2
roles:
- "monasca-user"
monasca_metrics:
"dimensions":
"region": "RegionOne"
"service": "identity"
"hostname": "fake_host"
"url": "http://fake_host:5000/v2.0"
"metrics_per_tenant": 10
-
scenario:
MonascaMetrics.list_metrics: {}
runner:
constant:
times: 10
concurrency: 2
contexts:
users:
tenants: 2
users_per_tenant: 2
roles:
- "monasca-user"
monasca_metrics:
"metrics_per_tenant": 10

View File

@@ -1,24 +0,0 @@
# 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(
"monasca_metric_create_prepoll_delay",
default=15.0,
deprecated_group="benchmark",
help="Delay between creating Monasca metrics and polling for "
"its elements.")
]}

View File

@@ -20,7 +20,6 @@ from rally_openstack.common.cfg import ironic
from rally_openstack.common.cfg import magnum
from rally_openstack.common.cfg import manila
from rally_openstack.common.cfg import mistral
from rally_openstack.common.cfg import monasca
from rally_openstack.common.cfg import neutron
from rally_openstack.common.cfg import nova
from rally_openstack.common.cfg import octavia
@@ -43,7 +42,7 @@ def list_opts():
opts = {}
for l_opts in (cinder.OPTS, heat.OPTS, ironic.OPTS, magnum.OPTS,
manila.OPTS, mistral.OPTS, monasca.OPTS,
manila.OPTS, mistral.OPTS,
nova.OPTS, osclients.OPTS, profiler.OPTS,
vm.OPTS, glance.OPTS, watcher.OPTS, tempest.OPTS,
keystone_roles.OPTS, keystone_users.OPTS, cleanup.OPTS,

View File

@@ -55,7 +55,6 @@ class _Service(utils.ImmutableMixin, utils.EnumMixin):
NEUTRON = "neutron"
DESIGNATE = "designate"
CEILOMETER = "ceilometer"
MONASCA = "monasca"
S3 = "s3"
TROVE = "trove"
SWIFT = "swift"
@@ -85,7 +84,6 @@ class _ServiceType(utils.ImmutableMixin, utils.EnumMixin):
NETWORK = "network"
DNS = "dns"
METERING = "metering"
MONITORING = "monitoring"
S3 = "s3"
DATABASE = "database"
OBJECT_STORE = "object-store"
@@ -115,7 +113,6 @@ class _ServiceType(utils.ImmutableMixin, utils.EnumMixin):
self.NETWORK: _Service.NEUTRON,
self.DNS: _Service.DESIGNATE,
self.METERING: _Service.CEILOMETER,
self.MONITORING: _Service.MONASCA,
self.S3: _Service.S3,
self.DATABASE: _Service.TROVE,
self.OBJECT_STORE: _Service.SWIFT,

View File

@@ -707,28 +707,6 @@ class Swift(OSClient):
return client
@configure("monasca", default_version="2_0",
default_service_type="monitoring", supported_versions=["2_0"])
class Monasca(OSClient):
"""Wrapper for MonascaClient which returns an authenticated native client.
"""
def create_client(self, version=None, service_type=None):
"""Return monasca client."""
from monascaclient import client as monasca
# Change this to use session once it's supported by monascaclient
client = monasca.Client(
self.choose_version(version),
self._get_endpoint(service_type),
token=self.keystone.auth_ref.auth_token,
timeout=CONF.openstack_client_http_timeout,
insecure=self.credential.https_insecure,
**self._get_auth_info(project_name_key="tenant_name"))
return client
@configure("magnum", default_version="1", supported_versions=["1"],
default_service_type="container-infra",)
class Magnum(OSClient):

View File

@@ -1,101 +0,0 @@
# 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 utils as rutils
from rally.common import validation
from rally_openstack.common import consts
from rally_openstack.task import context
from rally_openstack.task.scenarios.monasca import utils as monasca_utils
@validation.add("required_platform", platform="openstack", users=True)
@context.configure(name="monasca_metrics", platform="openstack", order=510)
class MonascaMetricGenerator(context.OpenStackContext):
"""Creates Monasca Metrics."""
CONFIG_SCHEMA = {
"type": "object",
"$schema": consts.JSON_SCHEMA,
"properties": {
"name": {
"type": "string"
},
"dimensions": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"service": {
"type": "string"
},
"hostname": {
"type": "string"
},
"url": {
"type": "string"
}
},
"additionalProperties": False
},
"metrics_per_tenant": {
"type": "integer",
"minimum": 1
},
"value_meta": {
"type": "array",
"items": {
"type": "object",
"properties": {
"value_meta_key": {
"type": "string"
},
"value_meta_value": {
"type": "string"
}
},
"additionalProperties": False
}
}
},
"additionalProperties": False
}
DEFAULT_CONFIG = {
"metrics_per_tenant": 2
}
def setup(self):
new_metric = {}
if "dimensions" in self.config:
new_metric = {
"dimensions": self.config["dimensions"]
}
for user, tenant_id in self._iterate_per_tenants():
scenario = monasca_utils.MonascaScenario(
context={"user": user, "task": self.context["task"]}
)
for i in range(self.config["metrics_per_tenant"]):
scenario._create_metrics(**new_metric)
rutils.interruptable_sleep(0.001)
rutils.interruptable_sleep(
monasca_utils.CONF.openstack.monasca_metric_create_prepoll_delay,
atomic_delay=1)
def cleanup(self):
# We don't have API for removal of metrics
pass

View File

@@ -12,8 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from rally_openstack.common import consts
from rally.task import atomic
from rally.task import validation
@@ -167,24 +165,3 @@ class ValidateHeat(scenario.OpenStackScenario):
with atomic.ActionTimer(self, "authenticate.validate_heat"):
for i in range(repetitions):
list(heat_client.stacks.list(limit=0))
@validation.add("number", param_name="repetitions", minval=1)
@validation.add("required_platform", platform="openstack", users=True)
@validation.add("required_services",
services=[consts.Service.MONASCA])
@scenario.configure(name="Authenticate.validate_monasca", platform="openstack")
class ValidateMonasca(scenario.OpenStackScenario):
def run(self, repetitions):
"""Check Monasca Client to ensure validation of token.
Creation of the client does not ensure validation of the token.
We have to do some minimal operation to make sure token gets validated.
:param repetitions: number of times to validate
"""
monasca_client = self.clients("monasca")
with atomic.ActionTimer(self, "authenticate.validate_monasca"):
for i in range(repetitions):
list(monasca_client.metrics.list(limit=0))

View File

@@ -1,37 +0,0 @@
# 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.task import validation
from rally_openstack.common import consts
from rally_openstack.task import scenario
from rally_openstack.task.scenarios.monasca import utils as monascautils
"""Scenarios for monasca Metrics API."""
@validation.add("required_services",
services=[consts.Service.MONASCA])
@validation.add("required_platform", platform="openstack", users=True)
@scenario.configure(name="MonascaMetrics.list_metrics", platform="openstack")
class ListMetrics(monascautils.MonascaScenario):
def run(self, **kwargs):
"""Fetch user's metrics.
:param kwargs: optional arguments for list query:
name, dimensions, start_time, etc
"""
self._list_metrics(**kwargs)

View File

@@ -1,54 +0,0 @@
# 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 random
import time
import uuid
from rally.common import cfg
from rally.task import atomic
from rally_openstack.task import scenario
CONF = cfg.CONF
class MonascaScenario(scenario.OpenStackScenario):
"""Base class for Monasca scenarios with basic atomic actions."""
@atomic.action_timer("monasca.list_metrics")
def _list_metrics(self, **kwargs):
"""Get list of user's metrics.
:param kwargs: optional arguments for list query:
name, dimensions, start_time, etc
:returns list of monasca metrics
"""
return self.clients("monasca").metrics.list(**kwargs)
@atomic.action_timer("monasca.create_metrics")
def _create_metrics(self, **kwargs):
"""Create user metrics.
:param kwargs: attributes for metric creation:
name, dimension, timestamp, value, etc
"""
timestamp = int(time.time() * 1000)
kwargs.update({"name": self.generate_random_name(),
"timestamp": timestamp,
"value": random.random(),
"value_meta": {
"key": str(uuid.uuid4())[:10]}})
self.clients("monasca").metrics.create(**kwargs)

View File

@@ -19,7 +19,6 @@ python-keystoneclient!=2.1.0 # Apache Software License
python-magnumclient # Apache Software License
python-manilaclient # Apache Software License
python-mistralclient!=3.2.0 # Apache Software License
python-monascaclient # Apache Software License
python-neutronclient # Apache Software License
python-novaclient # Apache License, Version 2.0
python-octaviaclient # Apache Software License

View File

@@ -1,32 +0,0 @@
{
"Dummy.openstack": [
{
"args": {
"sleep": 0.1
},
"runner": {
"type": "constant",
"times": 4,
"concurrency": 2
},
"context": {
"users": {
"tenants": 1,
"users_per_tenant": 1
},
"roles": [
"monasca-user"
],
"monasca_metrics": {
"dimensions": {
"region": "RegionOne",
"service": "identity",
"hostname": "fake_host",
"url": "http://fake_host:5000/v2.0"
},
"metrics_per_tenant": 10
}
}
}
]
}

View File

@@ -1,22 +0,0 @@
---
Dummy.openstack:
-
args:
sleep: 0.1
runner:
type: "constant"
times: 4
concurrency: 2
context:
users:
tenants: 1
users_per_tenant: 1
roles:
- "monasca-user"
monasca_metrics:
dimensions:
region: "RegionOne"
service: "identity"
hostname: "fake_host"
url: "http://fake_host:5000/v2.0"
metrics_per_tenant: 10

View File

@@ -1,25 +0,0 @@
{
"Authenticate.validate_monasca": [
{
"args": {
"repetitions": 2
},
"runner": {
"type": "constant",
"times": 10,
"concurrency": 5
},
"context": {
"users": {
"tenants": 3,
"users_per_tenant": 5
}
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@@ -1,16 +0,0 @@
---
Authenticate.validate_monasca:
-
args:
repetitions: 2
runner:
type: "constant"
times: 10
concurrency: 5
context:
users:
tenants: 3
users_per_tenant: 5
sla:
failure_rate:
max: 0

View File

@@ -1,38 +0,0 @@
{
"MonascaMetrics.list_metrics": [
{
"runner": {
"type": "constant",
"times": 10,
"concurrency": 1
},
"context": {
"users": {
"tenants": 1,
"users_per_tenant": 1
},
"roles": [
"monasca-user"
],
"monasca_metrics": {
"dimensions": {
"region": "RegionOne",
"service": "identity",
"hostname": "fake_host",
"url": "http://fake_host:5000/v2.0"
},
"metrics_per_tenant": 10
}
},
"args": {
"region": "RegionOne",
"limit": 5
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@@ -1,26 +0,0 @@
---
MonascaMetrics.list_metrics:
-
runner:
type: "constant"
times: 10
concurrency: 1
context:
users:
tenants: 1
users_per_tenant: 1
roles:
- "monasca-user"
monasca_metrics:
"dimensions":
"region": "RegionOne"
"service": "identity"
"hostname": "fake_host"
"url": "http://fake_host:5000/v2.0"
"metrics_per_tenant": 10
args:
"region": "RegionOne"
"limit": 5
sla:
failure_rate:
max: 0

View File

@@ -381,14 +381,6 @@ class Trove(ResourceManager):
return self.client.module.list(datastore="all")
class Monasca(ResourceManager):
REQUIRED_SERVICE = consts.Service.MONASCA
def list_metrics(self):
return self.client.metrics.list()
class Watcher(ResourceManager):
REQUIRED_SERVICE = consts.Service.WATCHER

View File

@@ -710,33 +710,6 @@ class OSClientsTestCase(test.TestCase):
mock_gnocchi.client.Client.assert_called_once_with(**kw)
self.assertEqual(fake_gnocchi, self.clients.cache["gnocchi"])
def test_monasca(self):
fake_monasca = fakes.FakeMonascaClient()
mock_monasca = mock.MagicMock()
mock_monasca.client.Client.return_value = fake_monasca
self.assertNotIn("monasca", self.clients.cache)
with mock.patch.dict("sys.modules",
{"monascaclient": mock_monasca}):
client = self.clients.monasca()
self.assertEqual(fake_monasca, client)
self.service_catalog.url_for.assert_called_once_with(
service_type="monitoring",
region_name=self.credential.region_name)
os_endpoint = self.service_catalog.url_for.return_value
kw = {"token": self.auth_ref.auth_token,
"timeout": cfg.CONF.openstack_client_http_timeout,
"insecure": False, "cacert": None,
"username": self.credential.username,
"password": self.credential.password,
"tenant_name": self.credential.tenant_name,
"auth_url": self.credential.auth_url
}
mock_monasca.client.Client.assert_called_once_with("2_0",
os_endpoint,
**kw)
self.assertEqual(mock_monasca.client.Client.return_value,
self.clients.cache["monasca"])
@mock.patch("%s.Ironic._get_endpoint" % PATH)
def test_ironic(self, mock_ironic__get_endpoint):
fake_ironic = fakes.FakeIronicClient()

View File

@@ -1050,12 +1050,6 @@ class FakeGnocchiClient(object):
self.metric = FakeMetricManager()
class FakeMonascaClient(object):
def __init__(self):
self.metrics = FakeMetricsManager()
class FakeNeutronClient(object):
def __init__(self, **kwargs):
@@ -1475,7 +1469,6 @@ class FakeClients(object):
self._trove = None
self._mistral = None
self._swift = None
self._monasca = None
self._ec2 = None
self._watcher = None
self._barbican = None
@@ -1528,11 +1521,6 @@ class FakeClients(object):
self._designate = FakeDesignateClient()
return self._designate
def monasca(self):
if not self._monasca:
self._monasca = FakeMonascaClient()
return self._monasca
def zaqar(self):
if not self._zaqar:
self._zaqar = FakeZaqarClient()

View File

@@ -1,99 +0,0 @@
# 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 unittest import mock
from rally_openstack.task.contexts.monasca import metrics
from rally_openstack.task.scenarios.monasca import utils as monasca_utils
from tests.unit import test
CTX = "rally_openstack.task.contexts.monasca"
class MonascaMetricGeneratorTestCase(test.TestCase):
def _gen_tenants(self, count):
tenants = {}
for id in range(count):
tenants[str(id)] = {"name": str(id)}
return tenants
def _gen_context(self, tenants_count, users_per_tenant,
metrics_per_tenant):
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": mock.MagicMock()})
context = test.get_test_context()
context.update({
"config": {
"users": {
"tenants": tenants_count,
"users_per_tenant": users_per_tenant,
"concurrent": 10,
},
"monasca_metrics": {
"name": "fake-metric-name",
"dimensions": {
"region": "fake-region",
"service": "fake-identity",
"hostname": "fake-hostname",
"url": "fake-url"
},
"metrics_per_tenant": metrics_per_tenant,
},
"roles": [
"monasca-user"
]
},
"admin": {
"endpoint": mock.MagicMock()
},
"users": users,
"tenants": tenants
})
return tenants, context
@mock.patch("%s.metrics.rutils.interruptable_sleep" % CTX)
@mock.patch("%s.metrics.monasca_utils.MonascaScenario" % CTX)
def test_setup(self, mock_monasca_scenario, mock_interruptable_sleep):
tenants_count = 2
users_per_tenant = 4
metrics_per_tenant = 5
tenants, real_context = self._gen_context(
tenants_count, users_per_tenant, metrics_per_tenant)
monasca_ctx = metrics.MonascaMetricGenerator(real_context)
monasca_ctx.setup()
self.assertEqual(tenants_count, mock_monasca_scenario.call_count,
"Scenario should be constructed same times as "
"number of tenants")
self.assertEqual(metrics_per_tenant * tenants_count,
mock_monasca_scenario.return_value._create_metrics.
call_count,
"Total number of metrics created should be tenant"
"counts times metrics per tenant")
first_call = mock.call(0.001)
second_call = mock.call(monasca_utils.CONF.openstack.
monasca_metric_create_prepoll_delay,
atomic_delay=1)
self.assertEqual(
[first_call] * metrics_per_tenant * tenants_count + [second_call],
mock_interruptable_sleep.call_args_list,
"Method interruptable_sleep should be called tenant counts times "
"metrics plus one")

View File

@@ -34,8 +34,8 @@ class AuthenticateTestCase(test.ScenarioTestCase):
# NOTE(stpierre): We can't use assert_has_calls() here because
# that includes calls on the return values of the mock object
# as well. Glance (and Heat and Monasca, tested below) returns
# an iterator that the scenario wraps in list() in order to
# as well. Glance (and Heat, tested below) returns an iterator that
# the scenario wraps in list() in order to
# force glanceclient to actually make the API call, and this
# results in a bunch of call().__iter__() and call().__len__()
# calls that aren't matched if we use assert_has_calls().
@@ -93,12 +93,3 @@ class AuthenticateTestCase(test.ScenarioTestCase):
[mock.call(limit=0)] * 5)
self._test_atomic_action_timer(scenario_inst.atomic_actions(),
"authenticate.validate_heat")
def test_validate_monasca(self):
scenario_inst = authenticate.ValidateMonasca()
scenario_inst.run(5)
self.assertCountEqual(
self.clients("monasca").metrics.list.call_args_list,
[mock.call(limit=0)] * 5)
self._test_atomic_action_timer(scenario_inst.atomic_actions(),
"authenticate.validate_monasca")

View File

@@ -1,36 +0,0 @@
# 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 unittest import mock
import ddt
from rally_openstack.task.scenarios.monasca import metrics
from tests.unit import test
@ddt.ddt
class MonascaMetricsTestCase(test.ScenarioTestCase):
@ddt.data(
{"region": None},
{"region": "fake_region"},
)
@ddt.unpack
def test_list_metrics(self, region=None):
scenario = metrics.ListMetrics(self.context)
self.region = region
scenario._list_metrics = mock.MagicMock()
scenario.run(region=self.region)
scenario._list_metrics.assert_called_once_with(region=self.region)

View File

@@ -1,51 +0,0 @@
# 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 ddt
from rally_openstack.task.scenarios.monasca import utils
from tests.unit import test
@ddt.ddt
class MonascaScenarioTestCase(test.ScenarioTestCase):
def setUp(self):
super(MonascaScenarioTestCase, self).setUp()
self.scenario = utils.MonascaScenario(self.context)
self.kwargs = {
"dimensions": {
"region": "fake_region",
"hostname": "fake_host_name",
"service": "fake_service",
"url": "fake_url"
}
}
def test_list_metrics(self):
return_metric_value = self.scenario._list_metrics()
self.assertEqual(return_metric_value,
self.clients("monasca").metrics.list.return_value)
self._test_atomic_action_timer(self.scenario.atomic_actions(),
"monasca.list_metrics")
@ddt.data(
{"name": ""},
{"name": "fake_metric"},
)
@ddt.unpack
def test_create_metrics(self, name=None):
self.name = name
self.scenario._create_metrics(name=self.name, kwargs=self.kwargs)
self.assertEqual(1, self.clients("monasca").metrics.create.call_count)