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:
parent
e19cb128b0
commit
629f0df528
|
@ -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)
|
||||
|
|
|
@ -231,3 +231,6 @@ class QuotasNoOp(Quotas):
|
|||
|
||||
def rollback(self, context=None):
|
||||
pass
|
||||
|
||||
def check_deltas(cls, context, deltas, *count_args, **count_kwargs):
|
||||
pass
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue