Skip to content

Commit 2d6fea7

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add support for GET /v2/cache/nodes/{image_id}"
2 parents 677c0f5 + 2cab612 commit 2d6fea7

5 files changed

Lines changed: 190 additions & 0 deletions

File tree

glanceclient/tests/unit/v2/test_cache.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@
6464
'',
6565
),
6666
},
67+
'/v2/cache/nodes/3a4560a1-e585-443e-9b39-553b46ec92d1': {
68+
'GET': (
69+
{},
70+
['http://node1.example', 'http://node2.example'],
71+
),
72+
},
6773
}
6874

6975

@@ -133,3 +139,40 @@ def test_cache_not_supported(self, mock_has_version):
133139
mock_has_version.return_value = False
134140
self.assertRaises(exc.HTTPNotImplemented,
135141
self.controller.list)
142+
143+
@mock.patch.object(common_utils, 'has_version')
144+
def test_list_cached_nodes(self, mock_has_version):
145+
mock_has_version.return_value = True
146+
image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
147+
nodes = self.controller.list_cached_nodes(image_id)
148+
self.assertEqual(['http://node1.example', 'http://node2.example'],
149+
list(nodes))
150+
expect = [('GET', '/v2/cache/nodes/%s' % image_id, {}, None)]
151+
self.assertEqual(expect, self.api.calls)
152+
153+
@mock.patch.object(common_utils, 'has_version')
154+
def test_list_cached_nodes_empty(self, mock_has_version):
155+
mock_has_version.return_value = True
156+
image_id = 'df601a47-7251-4d20-84ae-07de335af424'
157+
dummy_fixtures = {
158+
'/v2/cache/nodes/%s' % image_id: {
159+
'GET': (
160+
{},
161+
[],
162+
),
163+
}
164+
}
165+
dummy_api = utils.FakeAPI(dummy_fixtures)
166+
dummy_controller = cache.Controller(dummy_api)
167+
nodes = dummy_controller.list_cached_nodes(image_id)
168+
self.assertEqual([], list(nodes))
169+
self.assertEqual(
170+
[('GET', '/v2/cache/nodes/%s' % image_id, {}, None)],
171+
dummy_api.calls)
172+
173+
@mock.patch.object(common_utils, 'has_version')
174+
def test_list_cached_nodes_not_supported(self, mock_has_version):
175+
mock_has_version.return_value = False
176+
self.assertRaises(exc.HTTPNotImplemented,
177+
self.controller.list_cached_nodes,
178+
'3a4560a1-e585-443e-9b39-553b46ec92d1')

glanceclient/tests/unit/v2/test_shell_v2.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3799,6 +3799,106 @@ def test_do_cache_list_endpoint_not_provided(self):
37993799
'Direct server endpoint needs to be provided. Do '
38003800
'not use loadbalanced or catalog endpoints.')
38013801

3802+
def test_do_cache_nodes_list(self):
3803+
args = argparse.Namespace(
3804+
id='3a4560a1-e585-443e-9b39-553b46ec92d1')
3805+
expected_nodes = ['http://node1.example', 'http://node2.example']
3806+
with mock.patch.object(
3807+
self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
3808+
mocked_list_nodes.return_value = expected_nodes
3809+
test_shell.do_cache_nodes_list(self.gc, args)
3810+
mocked_list_nodes.assert_called_once_with(args.id)
3811+
objs, fields = utils.print_list.call_args[0]
3812+
self.assertEqual(['Node Reference URL'], fields)
3813+
self.assertEqual(2, len(objs))
3814+
self.assertEqual('http://node1.example', objs[0].node_reference_url)
3815+
self.assertEqual('http://node2.example', objs[1].node_reference_url)
3816+
3817+
def test_do_cache_nodes_list_empty(self):
3818+
args = argparse.Namespace(
3819+
id='3a4560a1-e585-443e-9b39-553b46ec92d1')
3820+
with mock.patch.object(
3821+
self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
3822+
mocked_list_nodes.return_value = []
3823+
test_shell.do_cache_nodes_list(self.gc, args)
3824+
objs, fields = utils.print_list.call_args[0]
3825+
self.assertEqual(['Node Reference URL'], fields)
3826+
self.assertEqual(0, len(objs))
3827+
3828+
def test_do_cache_nodes_list_unsupported(self):
3829+
args = argparse.Namespace(
3830+
id='3a4560a1-e585-443e-9b39-553b46ec92d1')
3831+
with mock.patch.object(
3832+
self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
3833+
mocked_list_nodes.side_effect = exc.HTTPNotImplemented
3834+
self.assertRaises(exc.HTTPNotImplemented,
3835+
test_shell.do_cache_nodes_list,
3836+
self.gc, args)
3837+
3838+
def test_do_cache_nodes_list_forbidden(self):
3839+
image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
3840+
args = argparse.Namespace(id=image_id)
3841+
with mock.patch.object(
3842+
self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
3843+
mocked_list_nodes.side_effect = exc.HTTPForbidden
3844+
with mock.patch(
3845+
'glanceclient.common.utils.print_err') as mock_print_err:
3846+
test_shell.do_cache_nodes_list(self.gc, args)
3847+
mock_print_err.assert_called_once_with(
3848+
"You are not permitted to list cached nodes for image '%s'."
3849+
% image_id)
3850+
3851+
def test_do_cache_nodes_list_conflict(self):
3852+
image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
3853+
args = argparse.Namespace(id=image_id)
3854+
with mock.patch.object(
3855+
self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
3856+
mocked_list_nodes.side_effect = exc.HTTPConflict
3857+
with mock.patch(
3858+
'glanceclient.common.utils.print_err') as mock_print_err:
3859+
test_shell.do_cache_nodes_list(self.gc, args)
3860+
mock_print_err.assert_called_once_with(
3861+
"'%s': Unable to list cached nodes for image '%s'."
3862+
% (exc.HTTPConflict(), image_id))
3863+
3864+
def test_do_cache_nodes_list_not_found(self):
3865+
image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
3866+
args = argparse.Namespace(id=image_id)
3867+
with mock.patch.object(
3868+
self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
3869+
mocked_list_nodes.side_effect = exc.HTTPNotFound
3870+
with mock.patch(
3871+
'glanceclient.common.utils.print_err') as mock_print_err:
3872+
test_shell.do_cache_nodes_list(self.gc, args)
3873+
mock_print_err.assert_called_once_with(
3874+
"'%s': Unable to list cached nodes for image '%s'."
3875+
% (exc.HTTPNotFound(), image_id))
3876+
3877+
def test_do_cache_nodes_list_http_exception(self):
3878+
image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
3879+
args = argparse.Namespace(id=image_id)
3880+
with mock.patch.object(
3881+
self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
3882+
mocked_list_nodes.side_effect = exc.HTTPBadRequest
3883+
with mock.patch(
3884+
'glanceclient.common.utils.print_err') as mock_print_err:
3885+
test_shell.do_cache_nodes_list(self.gc, args)
3886+
mock_print_err.assert_called_once_with(
3887+
"'%s': Unable to list cached nodes for image '%s'."
3888+
% (exc.HTTPBadRequest(), image_id))
3889+
3890+
def test_do_cache_nodes_list_endpoint_not_provided(self):
3891+
args = argparse.Namespace(
3892+
id='3a4560a1-e585-443e-9b39-553b46ec92d1')
3893+
self.gc.endpoint_provided = False
3894+
with mock.patch('glanceclient.common.utils.exit') as mock_exit:
3895+
mock_exit.side_effect = self._mock_utils_exit
3896+
with self.assertRaises(SystemExit):
3897+
test_shell.do_cache_nodes_list(self.gc, args)
3898+
mock_exit.assert_called_once_with(
3899+
'Direct server endpoint needs to be provided. Do '
3900+
'not use loadbalanced or catalog endpoints.')
3901+
38023902
def _test_cache_queue(self, supported=True, forbidden=False,):
38033903
args = argparse.Namespace(id=['image1'])
38043904
with mock.patch.object(self.gc.cache, 'queue') as mocked_cache_queue:

glanceclient/v2/cache.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,10 @@ def queue(self, image_id):
6060
url = '/v2/cache/%s' % image_id
6161
resp, body = self.http_client.put(url)
6262
return body, resp
63+
64+
@utils.add_req_id_to_object()
65+
def list_cached_nodes(self, image_id):
66+
if self.is_supported('v2.14'):
67+
url = '/v2/cache/nodes/%s' % image_id
68+
resp, body = self.http_client.get(url)
69+
return body, resp

glanceclient/v2/shell.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import json
1717
import os
1818
import sys
19+
from types import SimpleNamespace
1920

2021
from oslo_utils import strutils
2122

@@ -1651,6 +1652,38 @@ def do_cache_list(gc, args):
16511652
utils.print_cached_images(cached_images)
16521653

16531654

1655+
@utils.arg('id', metavar='<IMAGE_ID>',
1656+
help=_('ID of the image.'))
1657+
def do_cache_nodes_list(gc, args):
1658+
"""List node reference URLs where an image is cached."""
1659+
if not gc.endpoint_provided:
1660+
utils.exit("Direct server endpoint needs to be provided. Do not use "
1661+
"loadbalanced or catalog endpoints.")
1662+
try:
1663+
nodes = gc.cache.list_cached_nodes(args.id)
1664+
except exc.HTTPForbidden:
1665+
msg = _("You are not permitted to list cached nodes for image '%s'.")
1666+
utils.print_err(msg % args.id)
1667+
return
1668+
except exc.HTTPConflict as e:
1669+
msg = _("'%s': Unable to list cached nodes for image '%s'.")
1670+
utils.print_err(msg % (e, args.id))
1671+
return
1672+
except exc.HTTPNotFound as e:
1673+
msg = _("'%s': Unable to list cached nodes for image '%s'.")
1674+
utils.print_err(msg % (e, args.id))
1675+
return
1676+
except exc.HTTPNotImplemented:
1677+
raise
1678+
except exc.HTTPException as e:
1679+
msg = _("'%s': Unable to list cached nodes for image '%s'.")
1680+
utils.print_err(msg % (e, args.id))
1681+
return
1682+
1683+
rows = [SimpleNamespace(node_reference_url=u) for u in nodes]
1684+
utils.print_list(rows, ['Node Reference URL'])
1685+
1686+
16541687
@utils.arg('id', metavar='<IMAGE_ID>', nargs='+',
16551688
help=_('ID of image(s) to queue for caching.'))
16561689
def do_cache_queue(gc, args):
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
features:
3+
- |
4+
Add support for the Glance image-cache API ``GET /v2/cache/nodes/{image_id}``
5+
(list node reference URLs where an image is cached when centralized caching
6+
is enabled). This adds the ``cache-nodes-list`` shell command and the
7+
``list_cached_nodes`` method on the v2 cache controller.

0 commit comments

Comments
 (0)