summaryrefslogtreecommitdiff
path: root/nova/tests/unit/test_quota.py
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2019-05-30 20:39:43 +0000
committerGerrit Code Review <review@openstack.org>2019-05-30 20:39:43 +0000
commit653515a45032811b6bc2f1d0fb651472005496ec (patch)
treed2a3720b2f32b22a9985af5d7984815fea47b5a5 /nova/tests/unit/test_quota.py
parentc5ee0b83414f0f938c99894d243f0e5e507feb00 (diff)
parent8354f42e20da952a6ab5612363afb77386886df0 (diff)
downloadnova-653515a45032811b6bc2f1d0fb651472005496ec.tar.gz
Merge "Count instances from mappings and cores/ram from placement"
Diffstat (limited to 'nova/tests/unit/test_quota.py')
-rw-r--r--nova/tests/unit/test_quota.py151
1 files changed, 151 insertions, 0 deletions
diff --git a/nova/tests/unit/test_quota.py b/nova/tests/unit/test_quota.py
index 1e2f6dbb6a..50e1be820f 100644
--- a/nova/tests/unit/test_quota.py
+++ b/nova/tests/unit/test_quota.py
@@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import ddt
import mock
from oslo_db.sqlalchemy import enginefacade
from six.moves import range
@@ -1956,3 +1957,153 @@ class NoopQuotaDriverTestCase(test.TestCase):
quota.QUOTAS._resources,
'test_project')
self.assertEqual(self.expected_settable_quotas, result)
+
+
+@ddt.ddt
+class QuotaCountTestCase(test.NoDBTestCase):
+ @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
+ 'get_usages_counts_for_quota')
+ def test_cores_ram_count_placement(self, mock_get_usages):
+ usages = quota._cores_ram_count_placement(
+ mock.sentinel.context, mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+ mock_get_usages.assert_called_once_with(
+ mock.sentinel.context, mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+ self.assertEqual(mock_get_usages.return_value, usages)
+
+ @mock.patch('nova.objects.InstanceMappingList.get_counts')
+ @mock.patch('nova.quota._cores_ram_count_placement')
+ def test_instances_cores_ram_count_api_db_placement(
+ self, mock_placement_count, mock_get_im_count):
+ # Fake response from placement with project and user usages of cores
+ # and ram.
+ mock_placement_count.return_value = {'project': {'cores': 2, 'ram': 4},
+ 'user': {'cores': 1, 'ram': 2}}
+ # Fake count of instances based on instance mappings in the API DB.
+ mock_get_im_count.return_value = {'project': {'instances': 2},
+ 'user': {'instances': 1}}
+
+ counts = quota._instances_cores_ram_count_api_db_placement(
+ mock.sentinel.context, mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+
+ mock_get_im_count.assert_called_once_with(
+ mock.sentinel.context, mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+ mock_placement_count.assert_called_once_with(
+ mock.sentinel.context, mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+ expected = {'project': {'instances': 2, 'cores': 2, 'ram': 4},
+ 'user': {'instances': 1, 'cores': 1, 'ram': 2}}
+ self.assertDictEqual(expected, counts)
+
+ @ddt.data((True, True),
+ (True, False),
+ (False, True),
+ (False, False))
+ @ddt.unpack
+ @mock.patch('nova.quota.LOG.warning')
+ @mock.patch('nova.quota._user_id_queued_for_delete_populated')
+ @mock.patch('nova.quota._instances_cores_ram_count_legacy')
+ @mock.patch('nova.quota._instances_cores_ram_count_api_db_placement')
+ def test_instances_cores_ram_count(self, quota_from_placement,
+ uid_qfd_populated,
+ mock_api_db_placement_count,
+ mock_legacy_count,
+ mock_uid_qfd_populated, mock_warn_log):
+ # Check that all the combinations of
+ # [quota]count_usage_from_placement (True/False) and
+ # user_id_queued_for_delete_populated (True/False) do the right things.
+
+ # Fake count of instances, cores, and ram.
+ expected = {'project': {'instances': 2, 'cores': 2, 'ram': 4},
+ 'user': {'instances': 1, 'cores': 1, 'ram': 2}}
+ mock_api_db_placement_count.return_value = expected
+ mock_legacy_count.return_value = expected
+ # user_id and queued_for_delete populated/migrated (True/False)
+ mock_uid_qfd_populated.return_value = uid_qfd_populated
+ # Counting quota usage from placement enabled (True/False)
+ self.flags(count_usage_from_placement=quota_from_placement,
+ group='quota')
+
+ counts = quota._instances_cores_ram_count(
+ mock.sentinel.context, mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+
+ if quota_from_placement and uid_qfd_populated:
+ # If we are counting quota usage from placement and user_id and
+ # queued_for_delete data has all been migrated, we should count
+ # instances from the API DB using instance mappings and count
+ # cores and ram from placement.
+ mock_api_db_placement_count.assert_called_once_with(
+ mock.sentinel.context, mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+ # We should not have called the legacy counting method.
+ mock_legacy_count.assert_not_called()
+ # We should not have logged a warn message saying we were falling
+ # back to the legacy counting method.
+ mock_warn_log.assert_not_called()
+ else:
+ # If counting quota usage from placement is not enabled or if
+ # user_id or queued_for_delete data has not all been migrated yet,
+ # we should use the legacy counting method.
+ mock_legacy_count.assert_called_once_with(
+ mock.sentinel.context, mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+ # We should have logged a warn message saying we were falling back
+ # to the legacy counting method.
+ if quota_from_placement:
+ # We only log the message if someone has opted in to counting
+ # from placement.
+ mock_warn_log.assert_called_once()
+ # We should not have called the API DB and placement counting
+ # method.
+ mock_api_db_placement_count.assert_not_called()
+
+ self.assertDictEqual(expected, counts)
+
+ @mock.patch('nova.quota._user_id_queued_for_delete_populated')
+ @mock.patch('nova.quota._instances_cores_ram_count_legacy')
+ @mock.patch('nova.quota._instances_cores_ram_count_api_db_placement')
+ def test_user_id_queued_for_delete_populated_cache_by_project(
+ self, mock_api_db_placement_count, mock_legacy_count,
+ mock_uid_qfd_populated):
+ # We need quota usage from placement enabled to test this. For legacy
+ # counting, the cache is not used.
+ self.flags(count_usage_from_placement=True, group='quota')
+ # Fake count of instances, cores, and ram.
+ fake_counts = {'project': {'instances': 2, 'cores': 2, 'ram': 4},
+ 'user': {'instances': 1, 'cores': 1, 'ram': 2}}
+ mock_api_db_placement_count.return_value = fake_counts
+ mock_legacy_count.return_value = fake_counts
+
+ # First, check the case where user_id and queued_for_delete are found
+ # not to be migrated.
+ mock_uid_qfd_populated.return_value = False
+ quota._instances_cores_ram_count(mock.sentinel.context,
+ mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+ mock_uid_qfd_populated.assert_called_once()
+ # The second call should check for unmigrated records again, since the
+ # project was found not to be completely migrated last time.
+ quota._instances_cores_ram_count(mock.sentinel.context,
+ mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+ self.assertEqual(2, mock_uid_qfd_populated.call_count)
+
+ # Now check the case where the data migration was found to be complete.
+ mock_uid_qfd_populated.reset_mock()
+ mock_uid_qfd_populated.return_value = True
+ # The first call will check whether there are any unmigrated records.
+ quota._instances_cores_ram_count(mock.sentinel.context,
+ mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+ mock_uid_qfd_populated.assert_called_once()
+ # Second call should skip the check for user_id and queued_for_delete
+ # migrated because the result was cached.
+ mock_uid_qfd_populated.reset_mock()
+ quota._instances_cores_ram_count(mock.sentinel.context,
+ mock.sentinel.project_id,
+ user_id=mock.sentinel.user_id)
+ mock_uid_qfd_populated.assert_not_called()