Make quotas context cleanup lazy

*) Clean up service quotas only if they were changed.
*) Code clean up in setup() and cleanup() to avoid copy paste
*) Use append instead of extend in quotas tests
*) Init clients as well lazy
*) Add functional tests for quotas
*) Fix names of nova quotas
*) Fix don't break everything if you are not able to delete quota

Closes-bug: #1329907
Closes-bug: #1329795
Closes-bug: #1329379

Change-Id: I2ea781f08a731500bfff85e4c25e9786108b8b70
This commit is contained in:
Boris Pavlovic 2014-06-11 22:10:40 +04:00
parent 29eb4fb5a5
commit e973ced371
4 changed files with 185 additions and 111 deletions

View File

@ -1,4 +1,39 @@
---
Dummy.dummy:
-
args:
sleep: 0.01
runner:
type: "constant"
times: 1
concurrency: 1
context:
users:
tenants: 5
users_per_tenant: 5
quotas:
nova:
instances: 200
cores: 200
ram: -1
metadata_items: -1
injected_files: -1
injected_file_content_bytes: -1
injected_file_path_bytes: -1
key_pairs: 500
cinder:
gigabytes: -1
snapshots: -1
volumes: -1
neutron:
network: -1
subnet: -1
port: 200
router: 300
floatingip: -1
security_group: -1
security_group_rule: -1
NeutronNetworks.create_and_list_networks:
-
args:

View File

@ -101,7 +101,7 @@
Dummy.dummy:
-
args:
sleep: 5
sleep: 0.25
runner:
type: "constant"
times: 20
@ -110,6 +110,35 @@
users:
tenants: 1
users_per_tenant: 1
-
args:
sleep: 0.01
runner:
type: "constant"
times: 1
concurrency: 1
context:
users:
tenants: 5
users_per_tenant: 5
quotas:
nova:
instances: 200
cores: 200
ram: -1
floating_ips: 200
fixed_ips: 200
metadata_items: -1
injected_files: -1
injected_file_content_bytes: -1
injected_file_path_bytes: -1
key_pairs: 500
security_groups: 400
security_group_rules: 600
cinder:
gigabytes: -1
snapshots: -1
volumes: -1
Dummy.dummy_exception:
-

View File

@ -42,54 +42,54 @@ class NovaQuotas(object):
"type": "integer",
"minimum": -1
},
"floating-ips": {
"floating_ips": {
"type": "integer",
"minimum": -1
},
"fixed-ips": {
"fixed_ips": {
"type": "integer",
"minimum": -1
},
"metadata-items": {
"metadata_items": {
"type": "integer",
"minimum": -1
},
"injected-files": {
"injected_files": {
"type": "integer",
"minimum": -1
},
"injected-file-content-bytes": {
"injected_file_content_bytes": {
"type": "integer",
"minimum": -1
},
"injected-file-path-bytes": {
"injected_file_path_bytes": {
"type": "integer",
"minimum": -1
},
"key-pairs": {
"key_pairs": {
"type": "integer",
"minimum": -1
},
"security-groups": {
"security_groups": {
"type": "integer",
"minimum": -1
},
"security-group-rules": {
"security_group_rules": {
"type": "integer",
"minimum": -1
}
}
}
def __init__(self, client):
self.client = client
def __init__(self, clients):
self.clients = clients
def update(self, tenant_id, **kwargs):
self.client.quotas.update(tenant_id, **kwargs)
self.clients.nova().quotas.update(tenant_id, **kwargs)
def delete(self, tenant_id):
# Reset quotas to defaults and tag database objects as deleted
self.client.quotas.delete(tenant_id)
self.clients.nova().quotas.delete(tenant_id)
class CinderQuotas(object):
@ -114,11 +114,11 @@ class CinderQuotas(object):
}
}
def __init__(self, client):
self.client = client
def __init__(self, clients):
self.clients = clients
def update(self, tenant_id, **kwargs):
self.client.quotas.update(tenant_id, **kwargs)
self.clients.cinder().quotas.update(tenant_id, **kwargs)
def delete(self, tenant_id):
# Currently, no method to delete quotas available in cinder client:
@ -153,27 +153,27 @@ class NeutronQuotas(object):
"type": "integer",
"minimum": -1
},
"security-group": {
"security_group": {
"type": "integer",
"minimum": -1
},
"security-group-rule": {
"security_group_rule": {
"type": "integer",
"minimum": -1
}
}
}
def __init__(self, client):
self.client = client
def __init__(self, clients):
self.clients = clients
def update(self, tenant_id, **kwargs):
body = {"quota": kwargs}
self.client.update_quota(tenant_id, body=body)
self.clients.neutron().update_quota(tenant_id, body=body)
def delete(self, tenant_id):
# Reset quotas to defaults and tag database objects as deleted
self.client.delete_quota(tenant_id)
self.clients.neutron().delete_quota(tenant_id)
class Quotas(base.Context):
@ -197,29 +197,34 @@ class Quotas(base.Context):
def __init__(self, context):
super(Quotas, self).__init__(context)
self.clients = osclients.Clients(context["admin"]["endpoint"])
self.nova_quotas = NovaQuotas(self.clients.nova())
self.cinder_quotas = CinderQuotas(self.clients.cinder())
self.neutron_quotas = NeutronQuotas(self.clients.neutron())
self.manager = {
"nova": NovaQuotas(self.clients),
"cinder": CinderQuotas(self.clients),
"neutron": NeutronQuotas(self.clients)
}
def _service_has_quotas(self, service):
return len(self.config.get(service, {})) > 0
@utils.log_task_wrapper(LOG.info, _("Enter context: `quotas`"))
def setup(self):
for tenant in self.context["tenants"]:
if "nova" in self.config and len(self.config["nova"]) > 0:
self.nova_quotas.update(tenant["id"],
**self.config["nova"])
if "cinder" in self.config and len(self.config["cinder"]) > 0:
self.cinder_quotas.update(tenant["id"],
**self.config["cinder"])
if "neutron" in self.config and len(self.config["neutron"]) > 0:
self.neutron_quotas.update(tenant["id"],
**self.config["neutron"])
for service in self.manager:
if self._service_has_quotas(service):
self.manager[service].update(tenant["id"],
**self.config[service])
@utils.log_task_wrapper(LOG.info, _("Exit context: `quotas`"))
def cleanup(self):
for tenant in self.context["tenants"]:
# Always cleanup quotas before deleting a tenant
self.nova_quotas.delete(tenant["id"])
self.cinder_quotas.delete(tenant["id"])
self.neutron_quotas.delete(tenant["id"])
for service in self.manager:
if self._service_has_quotas(service):
for tenant in self.context["tenants"]:
try:
self.manager[service].delete(tenant["id"])
except Exception as e:
LOG.warning("Failed to remove quotas for tenant "
"%(tenant_id)s in service %(service)s "
"\n reason: %(exc)s"
% {"tenant_id": tenant["id"],
"service": service, "exc": e})

View File

@ -24,7 +24,7 @@ from tests import test
class NovaQuotasTestCase(test.TestCase):
@mock.patch("rally.benchmark.context.quotas.osclients.Clients.nova")
@mock.patch("rally.benchmark.context.quotas.osclients.Clients")
def test_update(self, client_mock):
nova_quotas = quotas.NovaQuotas(client_mock)
tenant_id = mock.MagicMock()
@ -32,31 +32,31 @@ class NovaQuotasTestCase(test.TestCase):
"instances": 10,
"cores": 100,
"ram": 100000,
"floating-ips": 100,
"fixed-ips": 10000,
"metadata-items": 5,
"injected-files": 5,
"injected-file-content-bytes": 2048,
"injected-file-path-bytes": 1024,
"key-pairs": 50,
"security-groups": 50,
"security-group-rules": 50
"floating_ips": 100,
"fixed_ips": 10000,
"metadata_items": 5,
"injected_files": 5,
"injected_file_content_bytes": 2048,
"injected_file_path_bytes": 1024,
"key_pairs": 50,
"security_groups": 50,
"security_group_rules": 50
}
nova_quotas.update(tenant_id, **quotas_values)
client_mock.quotas.update.assert_called_once_with(tenant_id,
**quotas_values)
client_mock.nova().quotas.update.assert_called_once_with(
tenant_id, **quotas_values)
@mock.patch("rally.benchmark.context.quotas.osclients.Clients.nova")
@mock.patch("rally.benchmark.context.quotas.osclients.Clients")
def test_delete(self, client_mock):
nova_quotas = quotas.NovaQuotas(client_mock)
tenant_id = mock.MagicMock()
nova_quotas.delete(tenant_id)
client_mock.quotas.delete.assert_called_once_with(tenant_id)
client_mock.nova().quotas.delete.assert_called_once_with(tenant_id)
class CinderQuotasTestCase(test.TestCase):
@mock.patch("rally.benchmark.context.quotas.osclients.Clients.cinder")
@mock.patch("rally.benchmark.context.quotas.osclients.Clients")
def test_update(self, client_mock):
cinder_quotas = quotas.CinderQuotas(client_mock)
tenant_id = mock.MagicMock()
@ -66,10 +66,10 @@ class CinderQuotasTestCase(test.TestCase):
"gigabytes": 1000
}
cinder_quotas.update(tenant_id, **quotas_values)
client_mock.quotas.update.assert_called_once_with(tenant_id,
**quotas_values)
client_mock.cinder().quotas.update.assert_called_once_with(
tenant_id, **quotas_values)
@mock.patch("rally.benchmark.context.quotas.osclients.Clients.cinder")
@mock.patch("rally.benchmark.context.quotas.osclients.Clients")
def test_delete(self, client_mock):
pass
# Currently, no method to delete quotas available in cinder client:
@ -82,7 +82,7 @@ class CinderQuotasTestCase(test.TestCase):
class NeutronQuotasTestCase(test.TestCase):
@mock.patch("rally.benchmark.context.quotas.osclients.Clients.neutron")
@mock.patch("rally.benchmark.context.quotas.osclients.Clients")
def test_update(self, client_mock):
neutron_quotas = quotas.NeutronQuotas(client_mock)
tenant_id = mock.MagicMock()
@ -92,19 +92,20 @@ class NeutronQuotasTestCase(test.TestCase):
"port": 100,
"router": 20,
"floatingip": 100,
"security-group": 100,
"security-group-rule": 100
"security_group": 100,
"security_group_rule": 100
}
neutron_quotas.update(tenant_id, **quotas_values)
body = {"quota": quotas_values}
client_mock.update_quota.assert_called_once_with(tenant_id, body=body)
client_mock.neutron().update_quota.assert_called_once_with(tenant_id,
body=body)
@mock.patch("rally.benchmark.context.quotas.osclients.Clients.neutron")
@mock.patch("rally.benchmark.context.quotas.osclients.Clients")
def test_delete(self, client_mock):
neutron_quotas = quotas.NeutronQuotas(client_mock)
tenant_id = mock.MagicMock()
neutron_quotas.delete(tenant_id)
client_mock.delete_quota.assert_called_once_with(tenant_id)
client_mock.neutron().delete_quota.assert_called_once_with(tenant_id)
class QuotasTestCase(test.TestCase):
@ -135,15 +136,15 @@ class QuotasTestCase(test.TestCase):
"instances": self.unlimited,
"cores": self.unlimited,
"ram": self.unlimited,
"floating-ips": self.unlimited,
"fixed-ips": self.unlimited,
"metadata-items": self.unlimited,
"injected-files": self.unlimited,
"injected-file-content-bytes": self.unlimited,
"injected-file-path-bytes": self.unlimited,
"key-pairs": self.unlimited,
"security-groups": self.unlimited,
"security-group-rules": self.unlimited
"floating_ips": self.unlimited,
"fixed_ips": self.unlimited,
"metadata_items": self.unlimited,
"injected_files": self.unlimited,
"injected_file_content_bytes": self.unlimited,
"injected_file_path_bytes": self.unlimited,
"key_pairs": self.unlimited,
"security_groups": self.unlimited,
"security_group_rules": self.unlimited
},
"neutron": {
"network": self.unlimited,
@ -151,8 +152,8 @@ class QuotasTestCase(test.TestCase):
"port": self.unlimited,
"router": self.unlimited,
"floatingip": self.unlimited,
"security-group": self.unlimited,
"security-group-rule": self.unlimited
"security_group": self.unlimited,
"security_group_rule": self.unlimited
}
}
for service in ctx["config"]["quotas"]:
@ -234,15 +235,15 @@ class QuotasTestCase(test.TestCase):
quotas_ctx.setup()
expected_setup_calls = []
for tenant in tenants:
expected_setup_calls.extend([mock.call()
.update(tenant["id"],
**cinder_quotas)])
expected_setup_calls.append(mock.call()
.update(tenant["id"],
**cinder_quotas))
mock_quotas.assert_has_calls(expected_setup_calls, any_order=True)
mock_quotas.reset_mock()
expected_cleanup_calls = []
for tenant in tenants:
expected_cleanup_calls.extend([mock.call().delete(tenant["id"])])
expected_cleanup_calls.append(mock.call().delete(tenant["id"]))
mock_quotas.assert_has_calls(expected_cleanup_calls, any_order=True)
@mock.patch("rally.benchmark.context.quotas.osclients.Clients")
@ -257,13 +258,13 @@ class QuotasTestCase(test.TestCase):
"ram": self.unlimited,
"floating-ips": self.unlimited,
"fixed-ips": self.unlimited,
"metadata-items": self.unlimited,
"injected-files": self.unlimited,
"injected-file-content-bytes": self.unlimited,
"injected-file-path-bytes": self.unlimited,
"key-pairs": self.unlimited,
"security-groups": self.unlimited,
"security-group-rules": self.unlimited,
"metadata_items": self.unlimited,
"injected_files": self.unlimited,
"injected_file_content_bytes": self.unlimited,
"injected_file_path_bytes": self.unlimited,
"key_pairs": self.unlimited,
"security_groups": self.unlimited,
"security_group_rules": self.unlimited,
}
}
@ -273,15 +274,15 @@ class QuotasTestCase(test.TestCase):
quotas_ctx.setup()
expected_setup_calls = []
for tenant in tenants:
expected_setup_calls.extend([mock.call()
.update(tenant["id"],
**nova_quotas)])
expected_setup_calls.append(mock.call()
.update(tenant["id"],
**nova_quotas))
mock_quotas.assert_has_calls(expected_setup_calls, any_order=True)
mock_quotas.reset_mock()
expected_cleanup_calls = []
for tenant in tenants:
expected_cleanup_calls.extend([mock.call().delete(tenant["id"])])
expected_cleanup_calls.append(mock.call().delete(tenant["id"]))
mock_quotas.assert_has_calls(expected_cleanup_calls, any_order=True)
@mock.patch("rally.benchmark.context.quotas.osclients.Clients")
@ -296,8 +297,8 @@ class QuotasTestCase(test.TestCase):
"port": self.unlimited,
"router": self.unlimited,
"floatingip": self.unlimited,
"security-group": self.unlimited,
"security-group-rule": self.unlimited
"security_group": self.unlimited,
"security_group_rule": self.unlimited
}
}
@ -307,15 +308,15 @@ class QuotasTestCase(test.TestCase):
quotas_ctx.setup()
expected_setup_calls = []
for tenant in tenants:
expected_setup_calls.extend([mock.call()
.update(tenant["id"],
**neutron_quotas)])
expected_setup_calls.append(mock.call()
.update(tenant["id"],
**neutron_quotas))
mock_quotas.assert_has_calls(expected_setup_calls, any_order=True)
mock_quotas.reset_mock()
expected_cleanup_calls = []
for tenant in tenants:
expected_cleanup_calls.extend([mock.call().delete(tenant["id"])])
expected_cleanup_calls.append(mock.call().delete(tenant["id"]))
mock_quotas.assert_has_calls(expected_cleanup_calls, any_order=True)
@mock.patch("rally.benchmark.context.quotas.osclients.Clients")
@ -333,17 +334,21 @@ class QuotasTestCase(test.TestCase):
self.assertFalse(mock_cinder_quotas.update.called)
self.assertFalse(mock_nova_quotas.update.called)
self.assertFalse(mock_neutron_quotas.update.called)
mock_nova_quotas.reset_mock()
mock_cinder_quotas.reset_mock()
mock_neutron_quotas.reset_mock()
tenants = ctx["tenants"]
expected_cleanup_calls = []
for tenant in tenants:
expected_cleanup_calls.extend([mock.call().delete(tenant["id"])])
mock_nova_quotas.assert_has_calls(expected_cleanup_calls,
any_order=True)
mock_cinder_quotas.assert_has_calls(expected_cleanup_calls,
any_order=True)
mock_neutron_quotas.assert_has_calls(expected_cleanup_calls,
any_order=True)
self.assertFalse(mock_cinder_quotas.delete.called)
self.assertFalse(mock_nova_quotas.delete.called)
self.assertFalse(mock_neutron_quotas.delete.called)
@mock.patch("rally.benchmark.context.quotas.NovaQuotas")
def test_exception_during_cleanup(self, mock_nova_quotas):
mock_nova_quotas.delete.side_effect = Exception("boom")
ctx = copy.deepcopy(self.context)
ctx["config"]["quotas"] = {"nova": {"cpu": 1}}
# NOTE(boris-42): ensure that cleanup didn't raise exceptions.
quotas.Quotas(ctx).cleanup()
self.assertEqual(mock_nova_quotas().delete.call_count,
len(self.context["tenants"]))