622bfb2e95
There are quite a few changes here as this is not only handling uuids for the hypervisor id but it's also a refactor in several APIs for consistency. The main changes are detailed in the REST API Version History doc in this change, but to summarize the changes: * Hypervisor and service IDs are handled as the UUIDs for those resources; this is necessary for accurately working with these resources across multiple cells. * The 'servers' and 'search' routes are deprecated and folded into the index and detail methods as query parameters, validated using json schema. * The show method will also be able to return the list of servers hosted on the given hypervisor using the with_servers query parameter. * The marker used when paging over lists of hypervisors is the compute node UUID. * Using the hypervisor_hostname_pattern query parameter will not work with paging parameters. * API reference docs are updated for the detailed changes. * Functional and unit tests are provided for all changes. Part of blueprint service-hyper-uuid-in-api Change-Id: I828350c179df8bcfa4739910abeafaba2f96982b
234 lines
9.9 KiB
Python
234 lines
9.9 KiB
Python
# Copyright (c) 2012 Rackspace Hosting
|
|
# 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.
|
|
"""
|
|
Tests For Cells Utility methods
|
|
"""
|
|
import inspect
|
|
import random
|
|
|
|
import mock
|
|
|
|
from nova.cells import utils as cells_utils
|
|
from nova import exception
|
|
from nova import objects
|
|
from nova import test
|
|
from nova.tests.unit import fake_instance
|
|
|
|
|
|
class CellsUtilsTestCase(test.NoDBTestCase):
|
|
"""Test case for Cells utility methods."""
|
|
def test_get_instances_to_sync(self):
|
|
fake_context = 'fake_context'
|
|
|
|
call_info = {'get_all': 0, 'shuffle': 0}
|
|
|
|
def random_shuffle(_list):
|
|
call_info['shuffle'] += 1
|
|
|
|
@staticmethod
|
|
def instance_get_all_by_filters(context, filters,
|
|
sort_key, sort_dir, limit, marker):
|
|
# Pretend we return a full list the first time otherwise we loop
|
|
# infinitely
|
|
if marker is not None:
|
|
return []
|
|
self.assertEqual(fake_context, context)
|
|
self.assertEqual('deleted', sort_key)
|
|
self.assertEqual('asc', sort_dir)
|
|
call_info['got_filters'] = filters
|
|
call_info['get_all'] += 1
|
|
instances = [fake_instance.fake_db_instance() for i in range(3)]
|
|
return instances
|
|
|
|
self.stub_out('nova.objects.InstanceList.get_by_filters',
|
|
instance_get_all_by_filters)
|
|
self.stub_out('random.shuffle', random_shuffle)
|
|
|
|
instances = cells_utils.get_instances_to_sync(fake_context)
|
|
self.assertTrue(inspect.isgenerator(instances))
|
|
self.assertEqual(3, len([x for x in instances]))
|
|
self.assertEqual(1, call_info['get_all'])
|
|
self.assertEqual({}, call_info['got_filters'])
|
|
self.assertEqual(0, call_info['shuffle'])
|
|
|
|
instances = cells_utils.get_instances_to_sync(fake_context,
|
|
shuffle=True)
|
|
self.assertTrue(inspect.isgenerator(instances))
|
|
self.assertEqual(3, len([x for x in instances]))
|
|
self.assertEqual(2, call_info['get_all'])
|
|
self.assertEqual({}, call_info['got_filters'])
|
|
self.assertEqual(1, call_info['shuffle'])
|
|
|
|
instances = cells_utils.get_instances_to_sync(fake_context,
|
|
updated_since='fake-updated-since')
|
|
self.assertTrue(inspect.isgenerator(instances))
|
|
self.assertEqual(3, len([x for x in instances]))
|
|
self.assertEqual(3, call_info['get_all'])
|
|
self.assertEqual({'changes-since': 'fake-updated-since'},
|
|
call_info['got_filters'])
|
|
self.assertEqual(1, call_info['shuffle'])
|
|
|
|
instances = cells_utils.get_instances_to_sync(fake_context,
|
|
project_id='fake-project',
|
|
updated_since='fake-updated-since', shuffle=True)
|
|
self.assertTrue(inspect.isgenerator(instances))
|
|
self.assertEqual(3, len([x for x in instances]))
|
|
self.assertEqual(4, call_info['get_all'])
|
|
self.assertEqual({'changes-since': 'fake-updated-since',
|
|
'project_id': 'fake-project'}, call_info['got_filters'])
|
|
self.assertEqual(2, call_info['shuffle'])
|
|
|
|
@mock.patch.object(objects.InstanceList, 'get_by_filters')
|
|
@mock.patch.object(random, 'shuffle')
|
|
def _test_get_instances_pagination(self, mock_shuffle,
|
|
mock_get_by_filters, shuffle=False, updated_since=None,
|
|
project_id=None):
|
|
fake_context = 'fake_context'
|
|
|
|
instances0 = objects.instance._make_instance_list(fake_context,
|
|
objects.InstanceList(),
|
|
[fake_instance.fake_db_instance() for i in range(3)],
|
|
expected_attrs=None)
|
|
marker0 = instances0[-1]['uuid']
|
|
instances1 = objects.instance._make_instance_list(fake_context,
|
|
objects.InstanceList(),
|
|
[fake_instance.fake_db_instance() for i in range(3)],
|
|
expected_attrs=None)
|
|
marker1 = instances1[-1]['uuid']
|
|
|
|
mock_get_by_filters.side_effect = [instances0, instances1, []]
|
|
|
|
instances = cells_utils.get_instances_to_sync(fake_context,
|
|
updated_since, project_id, shuffle=shuffle)
|
|
self.assertEqual(len([x for x in instances]), 6)
|
|
|
|
filters = {}
|
|
if updated_since is not None:
|
|
filters['changes-since'] = updated_since
|
|
if project_id is not None:
|
|
filters['project_id'] = project_id
|
|
limit = 100
|
|
expected_calls = [mock.call(fake_context, filters, sort_key='deleted',
|
|
sort_dir='asc', limit=limit, marker=None),
|
|
mock.call(fake_context, filters, sort_key='deleted',
|
|
sort_dir='asc', limit=limit, marker=marker0),
|
|
mock.call(fake_context, filters, sort_key='deleted',
|
|
sort_dir='asc', limit=limit, marker=marker1)]
|
|
mock_get_by_filters.assert_has_calls(expected_calls)
|
|
self.assertEqual(3, mock_get_by_filters.call_count)
|
|
|
|
def test_get_instances_to_sync_limit(self):
|
|
self._test_get_instances_pagination()
|
|
|
|
def test_get_instances_to_sync_shuffle(self):
|
|
self._test_get_instances_pagination(shuffle=True)
|
|
|
|
def test_get_instances_to_sync_updated_since(self):
|
|
self._test_get_instances_pagination(updated_since='fake-updated-since')
|
|
|
|
def test_get_instances_to_sync_multiple_params(self):
|
|
self._test_get_instances_pagination(project_id='fake-project',
|
|
updated_since='fake-updated-since', shuffle=True)
|
|
|
|
def test_split_cell_and_item(self):
|
|
path = 'australia', 'queensland', 'gold_coast'
|
|
cell = cells_utils.PATH_CELL_SEP.join(path)
|
|
item = 'host_5'
|
|
together = cells_utils.cell_with_item(cell, item)
|
|
self.assertEqual(cells_utils.CELL_ITEM_SEP.join([cell, item]),
|
|
together)
|
|
|
|
# Test normal usage
|
|
result_cell, result_item = cells_utils.split_cell_and_item(together)
|
|
self.assertEqual(cell, result_cell)
|
|
self.assertEqual(item, result_item)
|
|
|
|
# Test with no cell
|
|
cell = None
|
|
together = cells_utils.cell_with_item(cell, item)
|
|
self.assertEqual(item, together)
|
|
result_cell, result_item = cells_utils.split_cell_and_item(together)
|
|
self.assertEqual(cell, result_cell)
|
|
self.assertEqual(item, result_item)
|
|
|
|
def test_add_cell_to_compute_node(self):
|
|
fake_compute = objects.ComputeNode(id=1, host='fake')
|
|
cell_path = 'fake_path'
|
|
|
|
proxy = cells_utils.add_cell_to_compute_node(fake_compute, cell_path)
|
|
|
|
self.assertIsInstance(proxy, cells_utils.ComputeNodeProxy)
|
|
self.assertEqual(cells_utils.cell_with_item(cell_path, 1), proxy.id)
|
|
self.assertEqual(cells_utils.cell_with_item(cell_path, 'fake'),
|
|
proxy.host)
|
|
|
|
@mock.patch.object(objects.Service, 'obj_load_attr')
|
|
def test_add_cell_to_service_no_compute_node(self, mock_get_by_id):
|
|
fake_service = objects.Service(id=1, host='fake')
|
|
mock_get_by_id.side_effect = exception.ServiceNotFound(service_id=1)
|
|
cell_path = 'fake_path'
|
|
|
|
proxy = cells_utils.add_cell_to_service(fake_service, cell_path)
|
|
|
|
self.assertIsInstance(proxy, cells_utils.ServiceProxy)
|
|
self.assertEqual(cells_utils.cell_with_item(cell_path, 1), proxy.id)
|
|
self.assertEqual(cells_utils.cell_with_item(cell_path, 'fake'),
|
|
proxy.host)
|
|
self.assertRaises(AttributeError,
|
|
getattr, proxy, 'compute_node')
|
|
|
|
def test_add_cell_to_service_with_compute_node(self):
|
|
fake_service = objects.Service(id=1, host='fake')
|
|
fake_service.compute_node = objects.ComputeNode(id=1, host='fake')
|
|
cell_path = 'fake_path'
|
|
|
|
proxy = cells_utils.add_cell_to_service(fake_service, cell_path)
|
|
|
|
self.assertIsInstance(proxy, cells_utils.ServiceProxy)
|
|
self.assertEqual(cells_utils.cell_with_item(cell_path, 1), proxy.id)
|
|
self.assertEqual(cells_utils.cell_with_item(cell_path, 'fake'),
|
|
proxy.host)
|
|
self.assertRaises(AttributeError,
|
|
getattr, proxy, 'compute_node')
|
|
|
|
def test_proxy_object_serializer_to_primitive(self):
|
|
obj = objects.ComputeNode(id=1, host='fake')
|
|
obj_proxy = cells_utils.ComputeNodeProxy(obj, 'fake_path')
|
|
serializer = cells_utils.ProxyObjectSerializer()
|
|
|
|
primitive = serializer.serialize_entity('ctx', obj_proxy)
|
|
self.assertIsInstance(primitive, dict)
|
|
class_name = primitive.pop('cell_proxy.class_name')
|
|
cell_path = primitive.pop('cell_proxy.cell_path')
|
|
self.assertEqual('ComputeNodeProxy', class_name)
|
|
self.assertEqual('fake_path', cell_path)
|
|
self.assertEqual(obj.obj_to_primitive(), primitive)
|
|
|
|
def test_proxy_object_serializer_from_primitive(self):
|
|
obj = objects.ComputeNode(id=1, host='fake')
|
|
serializer = cells_utils.ProxyObjectSerializer()
|
|
|
|
# Recreating the primitive by hand to isolate the test for only
|
|
# the deserializing method
|
|
primitive = obj.obj_to_primitive()
|
|
primitive['cell_proxy.class_name'] = 'ComputeNodeProxy'
|
|
primitive['cell_proxy.cell_path'] = 'fake_path'
|
|
|
|
result = serializer.deserialize_entity('ctx', primitive)
|
|
self.assertIsInstance(result, cells_utils.ComputeNodeProxy)
|
|
self.assertEqual(obj.obj_to_primitive(),
|
|
result._obj.obj_to_primitive())
|
|
self.assertEqual('fake_path', result._cell_path)
|