Skip to content

Commit be27800

Browse files
committed
Add cache_image() support to the compute/{rpcapi,api,manager}
This provides the compute/api and compute/rpcapi plumbing to call cache_image() on a given compute node. It also defines the status information that we return to the caller to indicate what has been done. Related to blueprint image-precache-support Change-Id: If373fedb8d2e0dfc46b8ac5b018f8216aa5c643c
1 parent e46d59a commit be27800

File tree

6 files changed

+107
-2
lines changed

6 files changed

+107
-2
lines changed

nova/compute/manager.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ def update_compute_provider_status(self, context, rp_uuid, enabled):
521521
class ComputeManager(manager.Manager):
522522
"""Manages the running instances from creation to destruction."""
523523

524-
target = messaging.Target(version='5.3')
524+
target = messaging.Target(version='5.4')
525525

526526
def __init__(self, compute_driver=None, *args, **kwargs):
527527
"""Load configuration options and connect to the hypervisor."""
@@ -9235,6 +9235,44 @@ def _run_image_cache_manager_pass(self, context):
92359235

92369236
self.driver.manage_image_cache(context, filtered_instances)
92379237

9238+
def cache_images(self, context, image_ids):
9239+
"""Ask the virt driver to pre-cache a set of base images.
9240+
9241+
:param context: The RequestContext
9242+
:param image_ids: The image IDs to be cached
9243+
:return: A dict, keyed by image-id where the values are one of:
9244+
'cached' if the image was downloaded,
9245+
'existing' if the image was already in the cache,
9246+
'unsupported' if the virt driver does not support caching,
9247+
'error' if the virt driver raised an exception.
9248+
"""
9249+
9250+
results = {}
9251+
9252+
LOG.info('Caching %i image(s) by request', len(image_ids))
9253+
for image_id in image_ids:
9254+
try:
9255+
cached = self.driver.cache_image(context, image_id)
9256+
if cached:
9257+
results[image_id] = 'cached'
9258+
else:
9259+
results[image_id] = 'existing'
9260+
except NotImplementedError:
9261+
LOG.warning('Virt driver does not support image pre-caching;'
9262+
' ignoring request')
9263+
# NOTE(danms): Yes, technically we could short-circuit here to
9264+
# avoid trying the rest of the images, but it's very cheap to
9265+
# just keep hitting the NotImplementedError to keep the logic
9266+
# clean.
9267+
results[image_id] = 'unsupported'
9268+
except Exception as e:
9269+
results[image_id] = 'error'
9270+
LOG.error('Failed to cache image %(image_id)s: %(err)s',
9271+
{'image_id': image_id,
9272+
'err': e})
9273+
9274+
return results
9275+
92389276
@periodic_task.periodic_task(spacing=CONF.instance_delete_interval)
92399277
def _run_pending_deletes(self, context):
92409278
"""Retry any pending instance file deletes."""

nova/compute/rpcapi.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ class ComputeAPI(object):
370370
* 5.3 - Add migration and limits parameters to
371371
check_can_live_migrate_destination(), and a new
372372
drop_move_claim_at_destination() method
373+
* 5.4 - Add cache_images() support
373374
'''
374375

375376
VERSION_ALIASES = {
@@ -1234,3 +1235,17 @@ def trigger_crash_dump(self, ctxt, instance):
12341235
cctxt = client.prepare(server=_compute_host(None, instance),
12351236
version=version)
12361237
return cctxt.cast(ctxt, "trigger_crash_dump", instance=instance)
1238+
1239+
def cache_images(self, ctxt, host, image_ids):
1240+
version = '5.4'
1241+
client = self.router.client(ctxt)
1242+
if not client.can_send_version(version):
1243+
raise exception.NovaException('Compute RPC version pin does not '
1244+
'allow cache_images() to be called')
1245+
# This is a potentially very long-running call, so we provide the
1246+
# two timeout values which enables the call monitor in oslo.messaging
1247+
# so that this can run for extended periods.
1248+
cctxt = client.prepare(server=host, version=version,
1249+
call_monitor_timeout=CONF.rpc_response_timeout,
1250+
timeout=CONF.long_rpc_timeout)
1251+
return cctxt.call(ctxt, 'cache_images', image_ids=image_ids)

nova/conf/rpc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* live migration
3030
* scheduling
3131
* enabling/disabling a compute service
32+
* image pre-caching
3233
3334
Related options:
3435

nova/objects/service.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232

3333
# NOTE(danms): This is the global service version counter
34-
SERVICE_VERSION = 40
34+
SERVICE_VERSION = 41
3535

3636

3737
# NOTE(danms): This is our SERVICE_VERSION history. The idea is that any
@@ -161,6 +161,8 @@
161161
# drop_move_claim_at_destination() method, and numa_live_migration
162162
# parameter to check_can_live_migrate_source()
163163
{'compute_rpc': '5.3'},
164+
# Version 41: Add cache_images() to compute rpcapi (version 5.4)
165+
{'compute_rpc': '5.4'},
164166
)
165167

166168

nova/tests/unit/compute/test_compute_mgr.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5826,6 +5826,39 @@ def test_finish_revert_resize_network_migrate_finish_not_neutron(self, _):
58265826
details={'ovs_hybrid_plug': True})],
58275827
[])
58285828

5829+
@mock.patch('nova.compute.manager.LOG')
5830+
def test_cache_images_unsupported(self, mock_log):
5831+
r = self.compute.cache_images(self.context, ['an-image'])
5832+
self.assertEqual({'an-image': 'unsupported'}, r)
5833+
mock_log.warning.assert_called_once_with(
5834+
'Virt driver does not support image pre-caching; ignoring request')
5835+
5836+
def test_cache_image_existing(self):
5837+
with mock.patch.object(self.compute.driver, 'cache_image') as c:
5838+
c.return_value = False
5839+
r = self.compute.cache_images(self.context, ['an-image'])
5840+
self.assertEqual({'an-image': 'existing'}, r)
5841+
5842+
def test_cache_image_downloaded(self):
5843+
with mock.patch.object(self.compute.driver, 'cache_image') as c:
5844+
c.return_value = True
5845+
r = self.compute.cache_images(self.context, ['an-image'])
5846+
self.assertEqual({'an-image': 'cached'}, r)
5847+
5848+
def test_cache_image_failed(self):
5849+
with mock.patch.object(self.compute.driver, 'cache_image') as c:
5850+
c.side_effect = test.TestingException('foo')
5851+
r = self.compute.cache_images(self.context, ['an-image'])
5852+
self.assertEqual({'an-image': 'error'}, r)
5853+
5854+
def test_cache_images_multi(self):
5855+
with mock.patch.object(self.compute.driver, 'cache_image') as c:
5856+
c.side_effect = [True, False]
5857+
r = self.compute.cache_images(self.context, ['one-image',
5858+
'two-image'])
5859+
self.assertEqual({'one-image': 'cached',
5860+
'two-image': 'existing'}, r)
5861+
58295862

58305863
class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
58315864
def setUp(self):

nova/tests/unit/compute/test_rpcapi.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,22 @@ def test_unshelve_instance(self):
683683
request_spec=self.fake_request_spec_obj,
684684
version='5.2')
685685

686+
def test_cache_image(self):
687+
self._test_compute_api('cache_images', 'call',
688+
host='host', image_ids=['image'],
689+
call_monitor_timeout=60, timeout=1800,
690+
version='5.4')
691+
692+
def test_cache_image_pinned(self):
693+
ctxt = context.RequestContext('fake_user', 'fake_project')
694+
rpcapi = compute_rpcapi.ComputeAPI()
695+
rpcapi.router.client = mock.Mock()
696+
mock_client = mock.MagicMock()
697+
rpcapi.router.client.return_value = mock_client
698+
mock_client.can_send_version.return_value = False
699+
self.assertRaises(exception.NovaException,
700+
rpcapi.cache_images, ctxt, 'host', ['image'])
701+
686702
def test_unshelve_instance_old_compute(self):
687703
ctxt = context.RequestContext('fake_user', 'fake_project')
688704
rpcapi = compute_rpcapi.ComputeAPI()

0 commit comments

Comments
 (0)