Count fixed ips to check quota

This changes fixed ips from a ReservableResource to a
CountableResource and replaces quota reserve/commit/rollback with
check_deltas accordingly. Note that fixed ip quota is only relevant to
nova-network and will be obsolete when nova-network is removed.

Part of blueprint cells-count-resources-to-check-quota-in-api

Change-Id: Ia9e8142435888f6bc600e40bc7b0bf24b19576fd
This commit is contained in:
melanie witt 2017-03-29 19:51:06 +00:00
parent e19cb128b0
commit 629f0df528
6 changed files with 225 additions and 193 deletions

View File

@ -798,17 +798,13 @@ class NetworkManager(manager.Manager):
quota_project, quota_user = quotas_obj.ids_from_instance(context,
instance)
try:
quotas.reserve(fixed_ips=1, project_id=quota_project,
user_id=quota_user)
cleanup.append(functools.partial(quotas.rollback, context))
quotas.check_deltas(context, {'fixed_ips': 1}, quota_project)
except exception.OverQuota as exc:
usages = exc.kwargs['usages']
used = (usages['fixed_ips']['in_use'] +
usages['fixed_ips']['reserved'])
count = exc.kwargs['usages']['fixed_ips']
LOG.warning(_LW("Quota exceeded for project %(pid)s, tried to "
"allocate fixed IP. %(used)s of %(allowed)s are "
"in use or are already reserved."),
{'pid': quota_project, 'used': used,
{'pid': quota_project, 'used': count,
'allowed': exc.kwargs['quotas']['fixed_ips']},
instance_uuid=instance_id)
raise exception.FixedIpLimitExceeded()
@ -855,6 +851,29 @@ class NetworkManager(manager.Manager):
cleanup.append(functools.partial(fip.disassociate, context))
# NOTE(melwitt): We recheck the quota after creating the object
# to prevent users from allocating more resources than their
# allowed quota in the event of a race. This is configurable
# because it can be expensive if strict quota limits are not
# required in a deployment.
if CONF.quota.recheck_quota:
try:
quotas.check_deltas(context, {'fixed_ips': 0},
quota_project)
except exception.OverQuota as exc:
# Cleanup of the fixed IP allocation occurs in the
# outermost catch-all except block.
count = exc.kwargs['usages']['fixed_ips']
allowed = exc.kwargs['quotas']['fixed_ips']
LOG.warning(_LW("Quota exceeded for project %(pid)s, "
"tried to allocate fixed IP. %(used)s "
"of %(allowed)s are in use or are "
"already reserved."),
{'pid': quota_project, 'used': count,
'allowed': allowed},
instance_uuid=instance_id)
raise exception.FixedIpLimitExceeded()
LOG.debug('Refreshing security group members for instance.',
instance=instance)
self._do_trigger_security_group_members_refresh_for_instance(
@ -887,7 +906,6 @@ class NetworkManager(manager.Manager):
self._teardown_network_on_host,
context, network))
quotas.commit()
if address is None:
# TODO(mriedem): should _setup_network_on_host return the addr?
LOG.debug('Fixed IP is setup on network %s but not returning '
@ -927,101 +945,79 @@ class NetworkManager(manager.Manager):
instance = objects.Instance.get_by_uuid(
context.elevated(read_deleted='yes'), instance_uuid)
quotas = self.quotas_cls(context=context)
quota_project, quota_user = quotas_obj.ids_from_instance(context,
instance)
try:
quotas.reserve(fixed_ips=-1, project_id=quota_project,
user_id=quota_user)
except Exception:
LOG.exception(_LE("Failed to update usages deallocating "
"fixed IP"))
self._do_trigger_security_group_members_refresh_for_instance(
instance_uuid)
try:
self._do_trigger_security_group_members_refresh_for_instance(
instance_uuid)
if self._validate_instance_zone_for_dns_domain(context, instance):
for n in self.instance_dns_manager.get_entries_by_address(
address, self.instance_dns_domain):
self.instance_dns_manager.delete_entry(n,
self.instance_dns_domain)
if self._validate_instance_zone_for_dns_domain(context, instance):
for n in self.instance_dns_manager.get_entries_by_address(
address, self.instance_dns_domain):
self.instance_dns_manager.delete_entry(n,
self.instance_dns_domain)
fixed_ip_ref.allocated = False
fixed_ip_ref.save()
fixed_ip_ref.allocated = False
fixed_ip_ref.save()
if teardown:
network = fixed_ip_ref.network
if teardown:
network = fixed_ip_ref.network
if CONF.force_dhcp_release:
dev = self.driver.get_dev(network)
# NOTE(vish): The below errors should never happen, but
# there may be a race condition that is causing
# them per
# https://code.launchpad.net/bugs/968457,
# so we log a message to help track down
# the possible race.
if not vif_id:
LOG.info(_LI("Unable to release %s because vif "
"doesn't exist"), address)
return
if CONF.force_dhcp_release:
dev = self.driver.get_dev(network)
# NOTE(vish): The below errors should never happen, but
# there may be a race condition that is causing
# them per
# https://code.launchpad.net/bugs/968457,
# so we log a message to help track down
# the possible race.
if not vif_id:
LOG.info(_LI("Unable to release %s because vif "
"doesn't exist"), address)
return
vif = objects.VirtualInterface.get_by_id(context, vif_id)
vif = objects.VirtualInterface.get_by_id(context, vif_id)
if not vif:
LOG.info(_LI("Unable to release %s because vif "
"object doesn't exist"), address)
return
if not vif:
LOG.info(_LI("Unable to release %s because vif "
"object doesn't exist"), address)
return
# NOTE(cfb): Call teardown before release_dhcp to ensure
# that the IP can't be re-leased after a release
# packet is sent.
self._teardown_network_on_host(context, network)
# NOTE(vish): This forces a packet so that the
# release_fixed_ip callback will
# get called by nova-dhcpbridge.
try:
self.network_rpcapi.release_dhcp(context,
instance.launched_on,
dev, address,
vif.address)
except exception.RPCPinnedToOldVersion:
# Fall back on previous behaviour of calling
# release_dhcp on the local driver
self.driver.release_dhcp(dev, address, vif.address)
except exception.NetworkDhcpReleaseFailed:
LOG.error(_LE("Error releasing DHCP for IP %(address)s"
" with MAC %(mac_address)s"),
{'address': address,
'mac_address': vif.address},
instance=instance)
# NOTE(yufang521247): This is probably a failed dhcp fixed
# ip. DHCPRELEASE packet sent to dnsmasq would not trigger
# dhcp-bridge to run. Thus it is better to disassociate
# such fixed ip here.
fixed_ip_ref = objects.FixedIP.get_by_address(
context, address)
if (instance_uuid == fixed_ip_ref.instance_uuid and
not fixed_ip_ref.leased):
LOG.debug('Explicitly disassociating fixed IP %s from '
'instance.', address,
instance_uuid=instance_uuid)
fixed_ip_ref.disassociate()
else:
# We can't try to free the IP address so just call teardown
self._teardown_network_on_host(context, network)
except Exception:
with excutils.save_and_reraise_exception():
# NOTE(cfb): Call teardown before release_dhcp to ensure
# that the IP can't be re-leased after a release
# packet is sent.
self._teardown_network_on_host(context, network)
# NOTE(vish): This forces a packet so that the
# release_fixed_ip callback will
# get called by nova-dhcpbridge.
try:
quotas.rollback()
except Exception:
LOG.warning(_LW("Failed to rollback quota for "
"deallocate fixed IP: %s"), address,
instance=instance)
self.network_rpcapi.release_dhcp(context,
instance.launched_on,
dev, address,
vif.address)
except exception.RPCPinnedToOldVersion:
# Fall back on previous behaviour of calling
# release_dhcp on the local driver
self.driver.release_dhcp(dev, address, vif.address)
except exception.NetworkDhcpReleaseFailed:
LOG.error(_LE("Error releasing DHCP for IP %(address)s"
" with MAC %(mac_address)s"),
{'address': address,
'mac_address': vif.address},
instance=instance)
# Commit the reservations
quotas.commit()
# NOTE(yufang521247): This is probably a failed dhcp fixed
# ip. DHCPRELEASE packet sent to dnsmasq would not trigger
# dhcp-bridge to run. Thus it is better to disassociate
# such fixed ip here.
fixed_ip_ref = objects.FixedIP.get_by_address(
context, address)
if (instance_uuid == fixed_ip_ref.instance_uuid and
not fixed_ip_ref.leased):
LOG.debug('Explicitly disassociating fixed IP %s from '
'instance.', address,
instance_uuid=instance_uuid)
fixed_ip_ref.disassociate()
else:
# We can't try to free the IP address so just call teardown
self._teardown_network_on_host(context, network)
def release_dhcp(self, context, dev, address, vif_address):
self.driver.release_dhcp(dev, address, vif_address)

View File

@ -231,3 +231,6 @@ class QuotasNoOp(Quotas):
def rollback(self, context=None):
pass
def check_deltas(cls, context, deltas, *count_args, **count_kwargs):
pass

View File

@ -1894,6 +1894,12 @@ def _server_group_count_members_by_user(context, group, user_id):
return {'user': {'server_group_members': len(instances)}}
def _fixed_ip_count(context, project_id):
# NOTE(melwitt): This assumes a single cell.
count = objects.FixedIPList.get_count_by_project(context, project_id)
return {'project': {'fixed_ips': count}}
def _server_group_count(context, project_id, user_id=None):
"""Get the counts of server groups in the database.
@ -1930,7 +1936,7 @@ resources = [
'security_groups'),
ReservableResource('floating_ips', '_sync_floating_ips',
'floating_ips'),
ReservableResource('fixed_ips', '_sync_fixed_ips', 'fixed_ips'),
CountableResource('fixed_ips', _fixed_ip_count, 'fixed_ips'),
AbsoluteResource('metadata_items', 'metadata_items'),
AbsoluteResource('injected_files', 'injected_files'),
AbsoluteResource('injected_file_content_bytes',

View File

@ -643,39 +643,92 @@ class FlatNetworkTestCase(test.TestCase):
self.assertEqual(0, res[1]['id'])
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
@mock.patch('nova.objects.quotas.Quotas.reserve')
@mock.patch('nova.objects.quotas.Quotas.check_deltas')
@mock.patch('nova.objects.quotas.ids_from_instance')
def test_allocate_calculates_quota_auth(self, util_method, reserve,
def test_allocate_calculates_quota_auth(self, util_method, check,
get_by_uuid):
inst = objects.Instance()
inst['uuid'] = uuids.instance
get_by_uuid.return_value = inst
usages = {'fixed_ips': {'in_use': 10, 'reserved': 1}}
reserve.side_effect = exception.OverQuota(overs='testing',
quotas={'fixed_ips': 10},
usages=usages)
check.side_effect = exception.OverQuota(overs='testing',
quotas={'fixed_ips': 10},
usages={'fixed_ips': 10})
util_method.return_value = ('foo', 'bar')
self.assertRaises(exception.FixedIpLimitExceeded,
self.network.allocate_fixed_ip,
self.context, 123, {'uuid': uuids.instance})
util_method.assert_called_once_with(self.context, inst)
@mock.patch('nova.objects.fixed_ip.FixedIP.get_by_address')
@mock.patch('nova.objects.quotas.Quotas.reserve')
@mock.patch('nova.objects.fixed_ip.FixedIP.disassociate')
@mock.patch('nova.objects.fixed_ip.FixedIP.associate_pool')
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
@mock.patch('nova.objects.quotas.Quotas.check_deltas')
@mock.patch('nova.objects.quotas.ids_from_instance')
def test_deallocate_calculates_quota_auth(self, util_method, reserve,
get_by_address):
inst = objects.Instance(uuid=uuids.instance)
fip = objects.FixedIP(instance_uuid=uuids.instance,
virtual_interface_id=1)
get_by_address.return_value = fip
def test_allocate_over_quota_during_recheck(self, util_method, check,
get_by_uuid, associate,
disassociate):
inst = objects.Instance()
inst['uuid'] = uuids.instance
get_by_uuid.return_value = inst
# Simulate a race where the first check passes and the recheck fails.
check.side_effect = [None, exception.OverQuota(
overs='fixed_ips', quotas={'fixed_ips': 10},
usages={'fixed_ips': 10})]
util_method.return_value = ('foo', 'bar')
# This will fail right after the reserve call when it tries
# to look up the fake instance we created above
self.assertRaises(exception.InstanceNotFound,
self.network.deallocate_fixed_ip,
self.context, '1.2.3.4', instance=inst)
util_method.assert_called_once_with(self.context, inst)
address = netaddr.IPAddress('1.2.3.4')
fip = objects.FixedIP(instance_uuid=inst.uuid,
address=address,
virtual_interface_id=1)
associate.return_value = fip
network = network_obj.Network._from_db_object(
self.context, network_obj.Network(), test_network.fake_network)
network.save = mock.MagicMock()
self.assertRaises(exception.FixedIpLimitExceeded,
self.network.allocate_fixed_ip,
self.context, inst.uuid, network)
self.assertEqual(2, check.call_count)
call1 = mock.call(self.context, {'fixed_ips': 1}, 'foo')
call2 = mock.call(self.context, {'fixed_ips': 0}, 'foo')
check.assert_has_calls([call1, call2])
# Verify we removed the fixed IP that was added after the first quota
# check passed.
disassociate.assert_called_once_with(self.context)
@mock.patch('nova.objects.fixed_ip.FixedIP.associate_pool')
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
@mock.patch('nova.objects.quotas.Quotas.check_deltas')
@mock.patch('nova.objects.quotas.ids_from_instance')
def test_allocate_no_quota_recheck(self, util_method, check, get_by_uuid,
associate):
# Disable recheck_quota.
self.flags(recheck_quota=False, group='quota')
inst = objects.Instance()
inst['uuid'] = uuids.instance
inst['display_name'] = 'test'
get_by_uuid.return_value = inst
util_method.return_value = ('foo', 'bar')
network = network_obj.Network._from_db_object(
self.context, network_obj.Network(), test_network.fake_network)
network.save = mock.MagicMock()
@mock.patch.object(self.network, '_setup_network_on_host')
@mock.patch.object(self.network, 'instance_dns_manager')
@mock.patch.object(self.network,
'_do_trigger_security_group_members_refresh_for_instance')
def _test(trigger, dns, setup):
self.network.allocate_fixed_ip(self.context, inst.uuid, network)
_test()
# check_deltas should have been called only once.
check.assert_called_once_with(self.context, {'fixed_ips': 1}, 'foo')
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
@mock.patch('nova.objects.fixed_ip.FixedIP.associate')
@ -948,6 +1001,39 @@ class VlanNetworkTestCase(test.TestCase):
network.vpn_private_address = '192.168.0.2'
self.network.allocate_fixed_ip(self.context, FAKEUUID, network)
@mock.patch('nova.objects.fixed_ip.FixedIP.associate_pool')
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
@mock.patch('nova.objects.QuotasNoOp.check_deltas')
@mock.patch('nova.objects.quotas.ids_from_instance')
def test_allocate_fixed_ip_super_call(self, mock_ids, mock_check, mock_get,
mock_associate):
# No code in the VlanManager actually calls
# NetworkManager.allocate_fixed_ip() at this time. This is just to
# test that if it did, it would call through the QuotasNoOp class.
inst = objects.Instance()
inst['uuid'] = uuids.instance
inst['display_name'] = 'test'
mock_get.return_value = inst
mock_ids.return_value = ('foo', 'bar')
network = network_obj.Network._from_db_object(
self.context, network_obj.Network(), test_network.fake_network)
network.save = mock.MagicMock()
@mock.patch.object(self.network, '_setup_network_on_host')
@mock.patch.object(self.network, 'instance_dns_manager')
@mock.patch.object(self.network,
'_do_trigger_security_group_members_refresh_for_instance')
def _test(trigger, dns, setup):
super(network_manager.VlanManager, self.network).allocate_fixed_ip(
self.context, FAKEUUID, network)
_test()
# Make sure we called the QuotasNoOp.check_deltas() for VlanManager.
self.assertEqual(2, mock_check.call_count)
@mock.patch('nova.network.manager.VlanManager._setup_network_on_host')
@mock.patch('nova.network.manager.VlanManager.'
'_validate_instance_zone_for_dns_domain')
@ -2671,22 +2757,6 @@ class CommonNetworkTestCase(test.TestCase):
# Determine networks to NAT based on lookup
self._test_init_host_dynamic_fixed_range(self.network)
@mock.patch('nova.objects.quotas.Quotas.rollback')
@mock.patch('nova.objects.fixed_ip.FixedIP.get_by_address')
@mock.patch('nova.network.manager.NetworkManager.'
'_do_trigger_security_group_members_refresh_for_instance')
def test_fixed_ip_cleanup_rollback(self, fake_trig,
fixed_get, rollback):
manager = network_manager.NetworkManager()
fake_trig.side_effect = test.TestingException
self.assertRaises(test.TestingException,
manager.deallocate_fixed_ip,
self.context, 'fake', 'fake',
instance=fake_inst(uuid=uuids.non_existent_uuid))
rollback.assert_called_once_with()
def test_fixed_cidr_out_of_range(self):
manager = network_manager.NetworkManager()
ctxt = context.get_admin_context()

View File

@ -1148,7 +1148,7 @@ object_data = {
'PciDevicePoolList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
'PowerVMLiveMigrateData': '1.1-ac0fdd26da685f12d7038782cabd393a',
'Quotas': '1.3-40fcefe522111dddd3e5e6155702cf4e',
'QuotasNoOp': '1.3-b19f8a5d187f75ccf372aa23c5f906a4',
'QuotasNoOp': '1.3-347a039fc7cfee7b225b68b5181e0733',
'RequestSpec': '1.8-35033ecef47a880f9a5e46e2269e2b97',
'ResourceClass': '1.0-e6b367e2cf1733c5f3526f20a3286fe9',
'ResourceClassList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',

View File

@ -517,7 +517,7 @@ class BaseResourceTestCase(test.TestCase):
resources, 'reserve', quota.QUOTAS._resources)
def test_valid_method_call_check_wrong_method_check(self):
resources = {'fixed_ips': 1}
resources = {'instances': 1}
self.assertRaises(exception.InvalidQuotaMethodUsage,
quota._valid_method_call_check_resources,
@ -2673,13 +2673,11 @@ class QuotaSqlAlchemyBase(test.TestCase):
instances=5,
cores=10,
ram=10 * 1024,
fixed_ips=5,
)
self.deltas = dict(
instances=2,
cores=4,
ram=2 * 1024,
fixed_ips=2,
)
def make_sync(res_name):
@ -2702,8 +2700,7 @@ class QuotaSqlAlchemyBase(test.TestCase):
self.addCleanup(restore_sync_functions)
for res_name in ('instances', 'cores', 'ram', 'fixed_ips',
'floating_ips'):
for res_name in ('instances', 'cores', 'ram', 'floating_ips'):
method_name = '_sync_%s' % res_name
sqa_api.QUOTA_SYNC_FUNCTIONS[method_name] = make_sync(res_name)
res = quota.ReservableResource(res_name, '_sync_%s' % res_name)
@ -2732,12 +2729,6 @@ class QuotaSqlAlchemyBase(test.TestCase):
in_use=2,
reserved=2 * 1024,
until_refresh=None),
dict(resource='fixed_ips',
project_id='test_project',
user_id=None,
in_use=2,
reserved=2,
until_refresh=None),
]
def fake_get_project_user_quota_usages(context, project_id, user_id):
@ -2862,29 +2853,23 @@ class QuotaSqlAlchemyBase(test.TestCase):
delta=4),
dict(resource='ram',
delta=2 * 1024),
dict(resource='fixed_ips',
project_id='test_project',
delta=2),
]
if usage_id_change:
reservations_list[0]["usage_id"] = self.usages_created['instances']
reservations_list[1]["usage_id"] = self.usages_created['cores']
reservations_list[2]["usage_id"] = self.usages_created['ram']
reservations_list[3]["usage_id"] = self.usages_created['fixed_ips']
else:
reservations_list[0]["usage_id"] = self.usages['instances']
reservations_list[1]["usage_id"] = self.usages['cores']
reservations_list[2]["usage_id"] = self.usages['ram']
reservations_list[3]["usage_id"] = self.usages['fixed_ips']
if delta_change:
reservations_list[0]["delta"] = -2
reservations_list[1]["delta"] = -4
reservations_list[2]["delta"] = -2 * 1024
reservations_list[3]["delta"] = -2
return reservations_list
def _init_usages(self, *in_use, **kwargs):
for i, option in enumerate(('instances', 'cores', 'ram', 'fixed_ips')):
for i, option in enumerate(('instances', 'cores', 'ram')):
self.init_usage('test_project', 'fake_user',
option, in_use[i], **kwargs)
return FakeContext('test_project', 'test_class')
@ -2902,11 +2887,10 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
0, 0)
self.assertEqual(self.sync_called, set(['instances', 'cores',
'ram', 'fixed_ips']))
'ram']))
self.usages_list[0]["in_use"] = 0
self.usages_list[1]["in_use"] = 0
self.usages_list[2]["in_use"] = 0
self.usages_list[3]["in_use"] = 0
self.compare_usage(self.usages_created, self.usages_list)
reservations_list = self._update_reservations_list(True)
self.compare_reservation(result, reservations_list)
@ -2918,11 +2902,10 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
5, 0)
self.assertEqual(self.sync_called, set(['instances', 'cores',
'ram', 'fixed_ips']))
'ram']))
self.usages_list[0]["until_refresh"] = 5
self.usages_list[1]["until_refresh"] = 5
self.usages_list[2]["until_refresh"] = 5
self.usages_list[3]["until_refresh"] = 5
self.compare_usage(self.usages, self.usages_list)
self.assertEqual(self.usages_created, {})
self.compare_reservation(result, self._update_reservations_list())
@ -2934,11 +2917,10 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
5, 0)
self.assertEqual(self.sync_called, set(['instances', 'cores',
'ram', 'fixed_ips']))
'ram']))
self.usages_list[0]["until_refresh"] = 5
self.usages_list[1]["until_refresh"] = 5
self.usages_list[2]["until_refresh"] = 5
self.usages_list[3]["until_refresh"] = 5
self.compare_usage(self.usages, self.usages_list)
self.assertEqual(self.usages_created, {})
self.compare_reservation(result, self._update_reservations_list())
@ -2954,7 +2936,7 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
0, max_age)
self.assertEqual(self.sync_called, set(['instances', 'cores',
'ram', 'fixed_ips']))
'ram']))
self.compare_usage(self.usages, self.usages_list)
self.assertEqual(self.usages_created, {})
self.compare_reservation(result, self._update_reservations_list())
@ -2969,7 +2951,6 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
self.usages_list[0]["in_use"] = 3
self.usages_list[1]["in_use"] = 3
self.usages_list[2]["in_use"] = 3
self.usages_list[3]["in_use"] = 3
self.compare_usage(self.usages, self.usages_list)
self.assertEqual(self.usages_created, {})
self.compare_reservation(result, self._update_reservations_list())
@ -2979,7 +2960,6 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
self.deltas["instances"] = -2
self.deltas["cores"] = -4
self.deltas["ram"] = -2 * 1024
self.deltas["fixed_ips"] = -2
result = sqa_api.quota_reserve(context, self.resources, self.quotas,
self.quotas, self.deltas, self.expire,
0, 0)
@ -2991,8 +2971,6 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
self.usages_list[1]["reserved"] = 0
self.usages_list[2]["in_use"] = 1 * 1024
self.usages_list[2]["reserved"] = 0
self.usages_list[3]["in_use"] = 1
self.usages_list[3]["reserved"] = 0
self.compare_usage(self.usages, self.usages_list)
self.assertEqual(self.usages_created, {})
reservations_list = self._update_reservations_list(False, True)
@ -3007,11 +2985,10 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
expected_kwargs = {'code': 500,
'usages': {'instances': {'reserved': 0, 'in_use': 4},
'ram': {'reserved': 0, 'in_use': 10240},
'fixed_ips': {'reserved': 0, 'in_use': 4},
'cores': {'reserved': 0, 'in_use': 8}},
'overs': ['cores', 'fixed_ips', 'instances', 'ram'],
'overs': ['cores', 'instances', 'ram'],
'quotas': {'cores': 10, 'ram': 10240,
'fixed_ips': 5, 'instances': 5}}
'instances': 5}}
self.assertEqual(e.kwargs, expected_kwargs)
else:
self.fail('Expected OverQuota failure')
@ -3022,8 +2999,6 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
self.usages_list[1]["reserved"] = 0
self.usages_list[2]["in_use"] = 10 * 1024
self.usages_list[2]["reserved"] = 0
self.usages_list[3]["in_use"] = 4
self.usages_list[3]["reserved"] = 0
self.compare_usage(self.usages, self.usages_list)
self.assertEqual(self.usages_created, {})
self.assertEqual(self.reservations_created, {})
@ -3039,8 +3014,6 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
self.usages_list[1]["reserved"] = 0
self.usages_list[2]["in_use"] = 1 * 1024
self.usages_list[2]["reserved"] = 0
self.usages_list[3]["in_use"] = 1
self.usages_list[3]["reserved"] = 0
self.compare_usage(self.usages, self.usages_list)
self.assertEqual(self.usages_created, {})
self.assertEqual(self.reservations_created, {})
@ -3056,8 +3029,6 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
self.usages_list[1]["reserved"] = 0
self.usages_list[2]["in_use"] = 10 * 1024
self.usages_list[2]["reserved"] = 0
self.usages_list[3]["in_use"] = 1
self.usages_list[3]["reserved"] = 0
self.compare_usage(self.usages, self.usages_list)
self.assertEqual(self.usages_created, {})
self.assertEqual(self.reservations_created, {})
@ -3067,7 +3038,6 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
self.deltas["instances"] = -2
self.deltas["cores"] = -4
self.deltas["ram"] = -2 * 1024
self.deltas["fixed_ips"] = -2
result = sqa_api.quota_reserve(context, self.resources, self.quotas,
self.quotas, self.deltas, self.expire,
0, 0)
@ -3079,8 +3049,6 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
self.usages_list[1]["reserved"] = 0
self.usages_list[2]["in_use"] = 20 * 1024
self.usages_list[2]["reserved"] = 0
self.usages_list[3]["in_use"] = 10
self.usages_list[3]["reserved"] = 0
self.compare_usage(self.usages, self.usages_list)
self.assertEqual(self.usages_created, {})
reservations_list = self._update_reservations_list(False, True)
@ -3089,7 +3057,7 @@ class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
class QuotaEngineUsageRefreshTestCase(QuotaSqlAlchemyBase):
def _init_usages(self, *in_use, **kwargs):
for i, option in enumerate(('instances', 'cores', 'ram', 'fixed_ips',
for i, option in enumerate(('instances', 'cores', 'ram',
'floating_ips')):
self.init_usage('test_project', 'fake_user',
option, in_use[i], **kwargs)
@ -3110,8 +3078,7 @@ class QuotaEngineUsageRefreshTestCase(QuotaSqlAlchemyBase):
# 0 instances user
# 1 cores user
# 2 ram user
# 3 fixed_ips project
# 4 floating_ips project
# 3 floating_ips project
self.usages_list.append(dict(resource='floating_ips',
project_id='test_project',
user_id=None,
@ -3124,7 +3091,6 @@ class QuotaEngineUsageRefreshTestCase(QuotaSqlAlchemyBase):
self.usages_list[1]['reserved'] = 0
self.usages_list[2]['reserved'] = 0
self.usages_list[3]['reserved'] = 0
self.usages_list[4]['reserved'] = 0
def fake_quota_get_all_by_project_and_user(context, project_id,
user_id):
@ -3173,12 +3139,9 @@ class QuotaEngineUsageRefreshTestCase(QuotaSqlAlchemyBase):
self.assertEqual(self.sync_called, set(['instances']))
# Compare the expected usages with the actual usages.
# Expect fixed_ips not to change since it is project scoped.
# Expect floating_ips not to change since it is project scoped.
self.usages_list[3]['in_use'] = 3
self.usages_list[3]['until_refresh'] = 5
# Expect floating_ips not to change since it is project scoped.
self.usages_list[4]['in_use'] = 3
self.usages_list[4]['until_refresh'] = 5
self.compare_usage(self.usages, self.usages_list)
# No usages were created.
@ -3194,12 +3157,9 @@ class QuotaEngineUsageRefreshTestCase(QuotaSqlAlchemyBase):
self.assertEqual(self.sync_called, set(['instances']))
# Compare the expected usages with the actual usages.
# Expect fixed_ips not to change since it is project scoped.
# Expect floating_ips not to change since it is project scoped.
self.usages_list[3]['in_use'] = 3
self.usages_list[3]['until_refresh'] = 5
# Expect floating_ips not to change since it is project scoped.
self.usages_list[4]['in_use'] = 3
self.usages_list[4]['until_refresh'] = 5
self.compare_usage(self.usages, self.usages_list)
# No usages were created.
@ -3231,7 +3191,7 @@ class QuotaEngineUsageRefreshTestCase(QuotaSqlAlchemyBase):
ctxt = context.get_admin_context()
quota.QUOTAS.usage_refresh(ctxt, 'test_project')
self.assertEqual(self.sync_called, set(['fixed_ips', 'floating_ips']))
self.assertEqual(self.sync_called, set(['floating_ips']))
# Compare the expected usages with the actual usages.
# Expect instances not to change since it is user scoped.
@ -3266,9 +3226,6 @@ class QuotaEngineUsageRefreshTestCase(QuotaSqlAlchemyBase):
# Expect ram not to change since it is user scoped.
self.usages_list[2]['in_use'] = 3
self.usages_list[2]['until_refresh'] = 5
# Expect fixed_ips not to change since it is not in the keys list.
self.usages_list[3]['in_use'] = 3
self.usages_list[3]['until_refresh'] = 5
self.compare_usage(self.usages, self.usages_list)
self.assertEqual(self.usages_created, {})
@ -3284,8 +3241,8 @@ class QuotaEngineUsageRefreshTestCase(QuotaSqlAlchemyBase):
# Compare the expected usages with the created usages.
# Expect floating_ips to be created and initialized to 0
self.usages_list[4]['in_use'] = 0
self.compare_usage(self.usages_created, self.usages_list[6:])
self.usages_list[3]['in_use'] = 0
self.compare_usage(self.usages_created, self.usages_list[3:])
self.assertEqual(len(self.usages_created), 1)
@ -3299,10 +3256,10 @@ class QuotaEngineUsageRefreshTestCase(QuotaSqlAlchemyBase):
def test_usage_refresh_invalid_user_key(self):
context = FakeContext('test_project', 'test_class')
# fixed_ips is a valid syncable project key,
# floating_ips is a valid syncable project key,
# but not a valid user key
self._test_exception(context, 'test_project', 'fake_user',
['fixed_ips'])
['floating_ips'])
def test_usage_refresh_non_syncable_user_key(self):
# security_group_rules is a valid user key, but not syncable